Getting the outer context of a Meteor Template - meteor

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:

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 };
}
});

Nested #Each values are not available outside of the context

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);
}
});

I lose data context when i generate template with parameters

when I generate subtemplate in #each helper and i add parameter, then i lose data context, what is normally visible.
I found workaround by passing data fields to template by
{{> productItem parameter="test" name=name details=details}}
, but for more complicated collections that would be very tiresome... isn't there better option to solve that problem ?
<template name="main">
{{#each products}}
{{> productItem parameter="test"}}
{{/each}}
</template>
<template name="productItem">
<div class="product">
<p>{{name}}</p>
<p>{{details}}</p>
<p>{{parameter}}</p>
</div>
</template>
And javascript :
Template.main.helpers({
products: function(){
return Products.find({});
}
});
you are creating a new context ( it doesn't magically merge everything ), but its easy enough to include the original context.
you go :-
{{> productItem product=this parameter="test"}}
then
<template name="productItem">
<div class="product">
<p>{{product.name}}</p>
<p>{{product.details}}</p>
<p>{{parameter}}</p>
</div>
</template>
or
<template name="productItem">
<div class="product">
{{#with product}}
<p>{{name}}</p>
<p>{{details}}</p>
{{/with}}
<p>{{parameter}}</p>
</div>
</template>

Template.dynamic is not passing data context

I have a list template (#each) in a package that I plan to use across many different collections. Since the template is in a package they are not easily customizable. So I figured this was a great example to use Template.dynamic. Everything works except passing data.
.. I pull the data into the routed page and manipulate the data to match the dynamic template.
Template.usersIndex.helpers({
items: function() {
var users = Meteor.users.find({}).fetch();
var items = users.filter(function(user) {
return user;
}).map(function(user){
return {
name: user.profile.name,
description: user.emails[0].address,
tidbit: "hello"
};
});
return items
}
});
... the data passes perfectly to the usersIndex template.
<template name="usersIndex">
<div id="gc-users-index-navbar">
<h2>Title</h2>
</div>
<div id="gc-users-index" class="inner-content">
{{> Template.dynamic template="strataIndexItem" data="items" }}
</div>
</template>
... But no dice, the dynamic template is rendered but no data.
<template name="themeIndex">
<div class="list-group">
{{#each items }}
<div class="list-group-item">
<div class="row-content">
<div class="least-content">{{tidbit}}</div>
<h4 class="list-group-item-heading">{{name}}</h4>
<p class="list-group-item-text">{{description}}</p>
</div>
</div>
<div class="list-group-separator"></div>
{{/each}}
</div>
</template>
You pass data as string?
{{> Template.dynamic template="strataIndexItem" data="items" }}
You should pass data as variable, without ""
{{> Template.dynamic template="strataIndexItem" data=items }}
Also check if your strataIndexItem template is named strataIndexItem:
<template name="strataIndexItem">
...
</template>

Rendering a template when value in Mongo equals certain value

So I want to render a template that will hold an image when the value of the score in my Players collection equals 500, it right now doesn't render at all even when a player score equals 500, do I need an if statement in my handlebars or something else?
Relevant code I made so far
client
foo.html
<body>
<div class="container">
{{> header}}
<div class="row-fluid">
<div class="span8">
{{> leaderboard}}
</div>
<div class="span4">
{{> champion}}
</div>
</div>
</div>
</body>
<template name="champion">
{{#each winners}}
{{> winner}}
{{/each}}
</template>
<template name="winner">
<img src="gold.jpg" alt="winner">
</template>
foo.js
Template.champion.winners = function () {
return Players.find({score: 500});
};
You marked the Template code as being on the server in your question, but the code with Template.winner.winners should be on the client, not the server. This is most likely the problem. Also, you have two templates named winner, although Meteor should throw an error on the command line if you have duplicate template names.
Finally, this isn't what you asked, but it may come handy for debugging too. You can detect whether the cursor is empty in your templates using Handlebars {{else}}:
{{#each winners}}
{{> winner}}
{{else}}
no winners!
{{/each}}

Resources