Dynamic Templates and Jquery Steps - meteor

I'm working to create a form wizard with jQuery steps. From step 1 to step 2 I need to be able to determine which template is loaded depending on the selection in step 1. I have this template. I would like a different template to be rendered as a session variable changes.
<template name = "selectFrame">
<div class = "container">
<div class = "frameCarousel">
{{> Template.dynamic template=active data=this}}
</div>
</div>
</template>
Each of the internal templates look like this template below.
<template name = "artLineFrame">
{{#each artLineFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{tag}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "classicFrame">
{{#each classicFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{alt}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "versionsFrame">
{{#each versionsFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{alt}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
<template name = "myHarmonyFrame">
{{#each myHarmonyFrames}}
<div class = "thumbnail">
<div class = "something">
<img data-src = "{{source}}" alt = "placeholder" class = "img-circle">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{tag}}" class = "btn btn-primary">Select</button>
</div>
</div>
{{/each}}
</template>
Active is a function that gets the value of the session variable and returns the name of the appropriate template.
Template.selectFrame.created = function() {
this.selectFrame = new ReactiveVar(null);
this.autorun(function(){
var templateName = Session.get('board');
console.log('##' + templateName);
Template.instance().selectFrame.set(templateName);
})
Tracker.flush();
}
Template.selectFrame.helpers({
'active' : function() {
var dynamicName = Template.instance().selectFrame.get();
return dynamicName;
}
})
The value of board, the session variable that determines which template will render, changes as it should but the only template that ever renders is the template that the default value of board is set too. Can anyone offer some help or some edits I need to make.

I ran into a similar problem.
Instead of using the session variable directly, I created a ReactiveVar and used that instead in the helper.
(to get access to ReactiveVar add it to your project with: meteor add reactive-var )
First I created a reactive variable to store the name of the template I wanted it to dynamically change to within Template.name.created:
this.templateName = new ReactiveVar(null);
then I created an autorun within the Template.name.rendered function to change this value to whatever the session variable was changed to:
this.autorun(function(){
var templateName = Session.get("template_name");
Template.instance().templateName.set(templateName);
})
And finally I changed the template helper to get the template name from the reactive variable:
dynamic_template: function(){
var dynamicName = Template.instance().templateName.get();
return dynamicName;
}
I hope this approach works for your particular case.

Related

How to pass data variable from each to the with in Meteor?

I am trying to pass git variable to settings which is wrapping with with as shown below
If we can see right now it is setting.gitlab but i want to make it dynamically like setting.git where git is a variable mentioned in each loop .
{{#each git in gitlabFields}}
{{#with settings.gitlab}}
<div data-value={{#index}}>{{git}}</div>
<div>hihi</div>
<div class="rc-user-info__row">
<div class="rc-input">
<label class="rc-input__label">
<div >
<div class="rc-input__title" style="display: inline-block;" >{{_ label}}{{equal default value '*'}}</div>
</div>
<!-- {{#each gitlabFields}} -->
<div id="dynamicFields">
<div class="rc-input__wrapper" >
<input type="text" name="{{git}}" value="{{value}}" class="rc-input__element js-input" disabled="{{./disabled}}"/>
</div>
</div>
<!-- {{/each}} -->
</label>
</div>
</div>
{{/with}}
{{/each}}
git variable is not accessible here settings.git
Its showing undefined .
Assuming settings is an accessible Object, you can write a helper, that resolves the value:
Template.myTemplate.helpers({
getGitSettings (settings, key) {
return settings[key]
}
})
If you want to decouple settings from the template or avoid passing it through the whole display list you can also define it within the Template module as private variable:
const gitSettings = { ... };
Template.myTemplate.helpers({
getGitSettings (settings, key) {
return gitSettings[key]
}
})
If this pattern is used among many Templates you can also define a global helper:
const gitSettings = { ... };
Template.registerHelper('gitSettings', function (key) {
return gitSettings[key]
})
and use it via
{{#each field in gitlabFields}}
{{#with gitSettings field}}...{{/with}}
{{/each}}

Re-render Blaze template on text change

I have a Meteor Blaze Template based on autoform.
<template name="patientForm">
<div class='mdl-cell mdl-cell--12-col'>
{{#autoForm id="insertUpdatePatientForm" collection="Patients" doc=selectedPatientDoc
type=formType validation="browser" template="semanticUI"}}
<div class='two fields'>
{{> afQuickField name="firstName"}}
{{> afQuickField name="lastName"}}
</div>
<div class='two fields'>
{{> afQuickField name="phn.type"}}
{{> afQuickField name="phn.value" class="ramq"}}
</div>
<div class='two fields'>
{{> afQuickField name="birthDate"}}
{{> afQuickField name="gender"}}
</div>
<button class="ui submit button" type="submit">Save</button>
<div class="ui error message"></div>
{{/autoForm}}
</div>
</template>
I want to handle the text change event for input with name phn.value. Based on the text, I want to auto-populate two other fields: gender and date of birth. I am doing it by changing the template data directly as follows:
Template.patientForm.events({
'change .ramq': function changeRAMQ(event, templateInstance) {
const { patient } = templateInstance.data;
if (patient.phn.type === 'RAMQ') {
const ramq = event.target.value;
const yy = parseInt(ramq.substr(4, 2), 10);
let mm = parseInt(ramq.substr(6, 2), 10);
const dd = parseInt(ramq.substr(8, 2), 10);
patient.gender = mm < 13 ? 'Male' : 'Female';
if (mm > 50) {
mm -= 50;
}
patient.birthDate = moment(new Date(yy, mm, dd)).format('YYYY-MM-DD');
}
},
});
I am getting the template data and directly modifying the gender and birthdate when the phn.value changes. However, the modified gender and birthdate does not re-render in the autoform / blaze template. Any way by which I can force re-render of Blaze template or alternate ways to effect changes to other controls in Blaze template?
You can't modify the template data directly (you can, but that's not reactive and will be overwritten). Where are you getting the template data from? A collection? a reactive variable? If so, modify the data there -- Blaze will notice the change and re-render.
Supposedly something like this will work:
Patients.update(templateInstance.data._id, {$set: {
birthDate: ..,
gender: ..
}});
To enable reactivity and thus the re-rendering of the fields you should use a ReactiveVar (or ReactiveDict)
You can do this like this:
Template.patientForm.onCreated(function(){
const instance = this;
instance.birthDate = new ReactiveVar()
});
And in your helpers and events you can use instance.birthDate.set() / get()
Template.patientForm.helpers({
birthDate() {
return Template.instance().birthDate.get()
}
});
Template.patientForm.events({
'click something'(event, instance){
....
instance.birthDate.set(value);
....
}
});

Passing variables through handlebars blockhelper

I want to pass and compile params in my custom block helper. I found out that the params are inside a hash-object, but how can I compile them into the partial?
I want the param flyoutClass to be compiled into my partial. Everything works fine but the place where the output of my param should be stays empty...
handlebars helper
module.exports.register = function (Handlebars, context) {
Handlebars.registerHelper('injectHtml', function(name, options) {
console.log(options.hash); //yeah my param
var partial = Handlebars.partials[name];
var template = Handlebars.compile(partial);
//var template = Handlebars.compile(partial)(options.hash); *
var output = template({"body": options.fn(this)});
return new Handlebars.SafeString(output);
//return new Handlebars.SafeString(output(options.hash)); *
//return new Handlebars.SafeString(partial(output)); *
})
};
I have already tried some things, but I always get that warning...
Warning: string is not a function
.hbs file
<div class="flyout {{flyoutClass}}">
<button>flyout-button</button>
<div class="flyout__content">
{{{body}}}
</div>
</div>
call my blockhelper
{{#injectHtml "flyout" flyoutClass='navigation__item'}}
<div class="wrapper">
<h3>Headline</h3>
<p>some copy</p>
<button>CTA</button>
</div
<div class="wrapper">
<h3>Headline</h3>
<p>some copy</p>
<button>CTA</button>
</div>
{{/injectHtml}}
UPTADE
And is it possible to pass from my blockhelper a param to another partial?
var output = template({
"addClass": options.hash.addClass,
"id": options.hash.id,
"body": options.fn(this)
});
I like to extend this partial with "id"
{{#injectHtml "flyout" flyoutClass='navigation__item'id='myUniqueID'}}
and also use it in my partial button
<div class="flyout {{flyoutClass}}">
{{>button btn="icon-text" id="{{id}}"/*[1]*/ icon="arrow-down"label="klick me"}}
<div class="flyout__content" aria-labelledby="{{id}}"/*[2]*/>
{{{body}}}
</div>
</div>
But at [1] the param isn't compiled, [2] works fine.
<div class="flyout navigation__item">
<a href="#" id="{{id}}"/*[1]*/ aria-expanded="false">
<div class="flyout__content" aria-labelledby="myUniqueID"/*[2]*/>
//html content
</div>
</div>
You can't "compile" the flyoutClass parameter into the partial template because you are intending this parameter to be dynamic. This means it is really just another parameter of your template data:
var output = template({
"flyoutClass": options.hash.flyoutClass,
"body": options.fn(this)
});
UPDATE
... but there is no way to add the params dynamicly??[sic]
If you wish to pass along all of the parameters passed to your helper, you could simply extend a template data object within your helper with the options.hash:
var data = Handlebars.Utils.extend({ body: options.fn(this) }, options.hash);
var output = template(data);

Meteor Blaze.renderWithData how to InsertAfter is it possible?

I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>
I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.

Meteor Template Events

I'm trying to get a hold of meteor still so there might be an easy answer to this and i'm hoping that is the case. I have this function which works and returns the correct id when my button is clicked.
$(document).ready(function(){
$("button").click(function(){
var selection = (this.id);
boardSpecs[0] = selection;
return boardSpecs;
});
});
I want to make this into a meteor click event, something like this.
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = event.target.id;
Session.set('boardType', boardType);
alert(boardType);
}
});
This is the template where the button exists.
<template name = "selectBoard">
<div class = "container">
<div class = "boardCarousel">
{{#each boardList}}
<div class = "span1">
<div class = "thumbnail">
<img data-src = "{{source}}" alt = "placeholder" class = "img-rounded">
<div class = "something">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" id = "{{id}}" class = "btn btn-primary">Select</button>
</div>
</div>
</div>
{{/each}}
</div>
</div>
Assuming that the button is part of your template, you're code is nearly right. The only different is that this won't point to your button, so you'll need to get it from the event:
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = event.target.id;
Session.set('boardType', boardType);
alert(boardType);
}
});
Let's make this easier. Your button is defined as:
<button type = "button" id = "{{id}}" class = "btn btn-primary">Select</button>
And your event handler is trying to get at the id of the button which is {{id}}.
If you use nested templates as follows:
<template name = "selectBoard">
<div class = "container">
<div class = "boardCarousel">
{{#each boardList}}
{{> board}}
{{/each}}
</div>
</div>
</template>
<template name="board">
<div class = "span1">
<div class = "thumbnail">
<img data-src = "{{source}}" alt = "placeholder" class = "img-rounded">
<div class = "something">
<h2>{{name}}</h2>
<p>{{description}}</p>
<button type = "button" class = "btn btn-primary">Select</button>
</div>
</div>
</div>
</template>
Then this in your event handler will be the data context of the individual board and you can simply write:
Template.selectBoard.events({
'click button' : function (event) {
event.preventDefault();
var boardType = this.id;
Session.set('boardType', boardType);
alert(boardType);
}
});
I'd argue that this is more Meteoric (to borrow an adjective from Python).
I'd also avoid using the variable name id because of the potential confusion with the natural MongoDB document identifier _id.
I ended up using a body event and it worked right away. Not sure why but it did.
Template.body.events({
'click #selected' : function(event){
event.preventDefault();
Session.set('board',event.target.id);
}
});

Resources