Nested #Each values are not available outside of the context - meteor

I'm having a nested each pair like this:
{{#each goal in goals}}
<template name="task">
{{#each goal in goals}}
{{#each task in relatedTasks goal}}
<li>
<span class="text task"><strong>{{task.taskName}}</strong> to {{goal.goalName}}<br> taskid: {{task._id}}
{{task.taskPostpone}}</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=task._id}}
Update {{task.taskName}}
{{/afModal}}
</li>
{{/each}}
{{/each}}
</template>
and would like to get the value of the task._id in my client.js like here:
Template.task.events({
'click .task': function() {
Session.set("selectedTask", this._id);
//console.log(this._id);
//console.log(goal._id);
console.log(task._id);
//console.log('Click event happened: this._id saved as selectedItem Session variable.');
}
});
When I click on a task I receive this error on the console: "undefined" and I don't really understand the reason behind. I did some research and found a possible solution: Maybe 'click .task': function(task) should receive the task context or input so it could be able to grasp the meaning of this._id.
I have a {{#afModal doc=task._id}} which also should receive the value of task._id and does not seem to work, although it is placed in the right context I think.
I have a feeling that the two issues are related somehow.

The problem is that the {{#each goal in goals}} loop syntax does not change the data context within the loop (see docs). It simply adds a goal variable for use in your spacebars template.
One solution would be to move the contents of the {{#each task in ...}} loop to another template like so.
<template name="task">
{{#each goal in goals}}
{{#each task in relatedTasks goal}}
{{> goalTask goal=goal task=task}}
{{/each}}
{{/each}}
</template>
<template name="goalTask">
<li>
<span class="text task">
<strong>{{task.taskName}}</strong>
to {{goal.goalName}}<br>
taskid: {{task._id}} {{task.taskPostpone}}
</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=task._id}}
Update {{task.taskName}}
{{/afModal}}
</li>
</template>
Your event handler would then look like this.
Template.goalTask.events({
'click .task': function() {
console.log(this.goal._id);
console.log(this.task._id);
}
});

This is a common problem with events in nested objects, how to get the data context of the object that was clicked on?
The simplest way to approach this problem is to create a template for each level of nesting. The proper context is then provided automatically.
<template name="goals">
{{#each goals}}
{{#each task}}
{{> task}}
{{/each}}
{{/each}}
</template>
<template name="task">
<li>
<span class="text task"><a href="#modal-taskedit" data-toggle="modal">
<strong>{{task.taskName}}</strong></a> to {{goal.goalName}}<br>
taskid: {{task._id}}{{task.taskPostpone}}</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=_id}}
Update {{task.taskName}}
{{/afModal}}
</li>
</template>
Then your event handler can be defined on the inner task template with the data context automatically being the individual task and not the data context of the outer template.
Template.task.events({
'click .task': function(){
Session.set("selectedTask", this._id);
}
});

Related

How to create reusable UI with variable template?

I want to do something like this:
Component file:
<template name="drop_down">
<span class="dropdown">
{{> primary_element}} <--- load the template named button
<span class="dropdown-panel">
{{> panel_template}} <--- load the template named button_panel
</span>
</span>
</template>
Usage:
{{> drop_down primary_element="button" panel_template="button_panel"}}
<template name="button"> some styled button </template>
<template name="button_panel"> panel content </template>
and then I can reuse it just like this
{{> drop_down primary_element="tmp_btn_2" panel_template="tmp_panel_2"}}
<template name="tmp_btn_2"> another button style </template>
<template name="tmp_panel_2"> other panel content </template>
You should be able to do this with dynamic templates. In blaze you can pass in the template to be used as a variable, as well as the data context itself. They are quite flexible.
For example:
{{> Template.dynamic template=whichTemplate data=myData }}
This would refer to the whichTemplate helper to figure out which template to use and would get the data context from the myData helper. i.e. both the choice of template and the choice of data come from variables.
In your case you are trying to use two dynamic templates in the context of your dropdown template. You can do:
<template name="drop_down">
{{#with myElements}}
<span class="dropdown">
{{> Template.dynamic template=this.primary}}
<span class="dropdown-panel">
{{> Template.dynamic template=this.panel}}
</span>
</span>
{{/with}}
</template
Then your myElements helper just needs to return the names of the templates to use as strings, ex:
Template.dropdown.helpers({
myElements() {
let p1 = "button"; // compute these two based on your own logic
let p2 = "button_panel";
return {primary: p1, panel: p2 };
}
});

re-rendering template when changing property from click event

i would like to trigger the visibility of a block in my handlebars template from an onclick function. i can use sessions and a helper function to get this to work but it seems overkill.
<template name="eventlist">
{{#each eventcollection}}
<div class="eventcontent">
name: {{name}} <br>
{{#if showdetail}}
detail: {{detail}}
{{/if}}
</div>
{{/each}}
</template>
could it be possible to make it work somehow like this?
Template.eventlist.events = {
'click .eventcontent': function() { this.showdetail = true}
}
meteor would just need to check if any attribute of this changed after the event completed and then rerender the template
Why bother Meteor? Such things were easy to achieve in the ancient Javascript days:
<template name="eventList">
{{#each eventCollection}}
<div class="eventContent">
name ...
<span class="eventHidden" style="display: none;">
detail ...
</span>
</div>
{{/each}}
</template>
Template.eventList.events({
'click .eventContent': function(e) {
$(e.target).find('.eventHidden').toggle();
},
});

in Meteor.template.rendered how to make a call after everything is rendered (after the {{#each}} loop finishes)

Hi I have this template
<template name="users">
{{#each user}}
<li id="{{_id}}">
<input type="checkbox" checked />
<span><select name="colorpicker">
{{#each color}}
<option value="{{mColorCode}}" {{selected ../mColor mColorCode}}>{{mColorName}}</option>
{{/each}}
</select>
</span>
<img width="40" src="data:image/png;base64,{{mImage}}" />
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</template>
What i want to do is after the template is rendered, i want to convert the dropdown to a colorpicker. I'm using a jquery plugin
Template.users.rendered = function(){
$('select[name="colorpicker"]').simplecolorpicker({picker: true});
}
The problem is sometimes its not working, (Sometimes the call is being made before the dom being ready.)
I want to call this plugin after everything is rendered. and not for each user added, how can i do this ?
Thanks
I've found Meteor to be a little funny with how often it renders templates, and jQuery functions can end up building up.
I've taken to adding console.log("users rendered"); to understand how many times and when the render callback is triggered.
One thing I've had some success with is wrapping that template inside another, and then tying the callback to the outside template. Something like this:
<template name="container">
{{> users}}
</template>
<template name="users">
{{#each user}}
<li id="{{_id}}">
<input type="checkbox" checked />
<span><select name="colorpicker">
{{#each color}}
<option value="{{mColorCode}}" {{selected ../mColor mColorCode}}>{{mColorName}}</option>
{{/each}}
</select>
</span>
<img width="40" src="data:image/png;base64,{{mImage}}" />
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</template>
And this just add the callback to the container
Template.container.rendered = function(){
$('select[name="colorpicker"]').simplecolorpicker({picker: true});
console.log("rendered");
}
Not totally sure why it works, but it has for me, hopefully someone can illuminate us both.
This is the solution that I used, it's also a hack if someone has a better solution please post it.
What i did was i put the rendered template in the callback of the subscribe of my collection.
So my code was something like this :
Meteor.subscribe('trackedUser',function(){
Template.users.rendered = function(){
......
}
}
I see this is a pretty old post, but since I had a problem and I think I've found a better way, here it is. The containing template will technically be rendered even while the contents are rendering. In my case I was trying to initialize a materialize modal. To make it work It had to do the following.
template.html
<div class="row" id="eventResults">
{{#each eventResults}}
{{> eventResult}}
{{/each}}
</div>
<template name="eventResult">
<div class="modal event" id="{{id}}">
<div class="modal-content">
...
and template.js
Template.eventResult.onRendered(function(){
this.$('.modal-trigger').leanModal();
By calling the code on the onRendered of the child element and using this.$('...'), the code doesn't get called multiple times for each element, just once each.

Getting the outer context of a Meteor Template

I have the following use-case: There are rooms which have beds inside. (Bummer...)
There is a loop of rooms which uses a template "room".
<template name="rooms">
{{#each availableRooms}}
{{> room}}
{{/each}}
</template>
This template gets for each iteration a room. This is accessible by this.
<template name="room">
<div class="room-outer">
<button type="button" class="btn" data-toggle="collapse" data-target="#list-{{_id}}">
{{name}} : {{getBeds this}} beds free.
</button>
<div id="list-{{_id}}" class="collapse in room-inner">
{{#each guests_id}}
<div class="bed">
<div class="blanket">
{{showUser this}}
</div>
</div>
{{/each}}
</div>
</div>
</template>
Now I like to calculate some special value which I do by extending the template. I need now to pass the this variable to the getBeds function. Is it possible to do this by grabing outside the template and get the room into the function?
Template.room.getBeds = function (room) {
if (room.guests_id)
return room.beds - _.size(room.guests_id);
else
return room.beds;
};
Basically I don't want to have to write {{getBeds this}} but only {{getBeds}}
Shouldn't this work?
Template.room.getBeds = function () {
if (this.guests_id)
return this.beds - _.size(this.guests_id);
else
return this.beds;
};
See the docs:
Helpers can take arguments, and they receive the current template data in this:

How to render a Meteor Template from Collection of Template names?

I have three simple Templates in Meteor, and a Collection on the server with any combination of their names. I want to be able to render these templates dynamically based on which of their names are in the Collection.
Currently I am trying to accomplish this by using the client to subscribe to the Collection, and access the names through a template function. Unfortunately, if I try to run ">" on the names, Meteor attempts to render the variable name instead of the Template pointed to by its value.
So instead of rendering the html in template1, template2, and template3, the output is merely their names on the page: "template1 template2 template3".
Here is the code I've been using, I hope there's a way to solve my issue without having to run Meteor.render manually.
Server js:
TemplatesToRender = new Meteor.Collection("templatesToRender");
TemplatesToRender.insert({templateName: "template3"});
TemplatesToRender.insert({templateName: "template2"});
Client html:
<body>
{{#each templatesToRender}}
{{> templateName}} // meteor trying to render a template
// called "templateName" instead of the
// variable inside templateName.
{{/each}}
</body>
<template name="template1">
<span>Template 1</span>
</template>
<template name="template2">
<span>Template 2</span>
</template>
<template name="template3">
<span>Template 3</span>
</template>
You can make a render helper:
Handlebars.registerHelper('render', function(name, options) {
if (Template[name])
return new Handlebars.SafeString(Template[name]());
});
And use it with
{{render templateName}}
You might want to try this
in your html
<body>
{{> templateToRender}}
</body>
<template name="templateToRender">
{{! use below to detect which template to render}}
{{#if templateName "template1"}}
{{> template1}}
{{/if}}
{{#if templateName "template2"}}
{{> template3}}
{{/if}}
{{#if templateName "template3"}}
{{> template3}}
{{/if}}
</template
<template name="template1">
<p>this is template1</p>
</template>
<template name="template2">
<p>this is template2</p>
</template>
<template name="template3">
<p>this is template3</p>
</template>
in your script
Template.templateToRender.templateName = (which) ->
# if user have a field like templateName you can do things like
tmplName = Meteor.user().templateName
# Session.equals will cause a template render if condition is true.
Session.equals which, tmplName
Meteor 1.0 just came out today, and I just want to update this for 2014 :)
https://docs.meteor.com/#/full/template_dynamic
{{> Template.dynamic template=template [data=data] }}
Sample Usage:
{{#each kitten}}
{{> Template.dynamic template=kitten_type data=this }}
{{/each}}

Resources