Passing variable from parent template to child template in meteor - templates

I have a parent template with several nested child templates inside it. And inside each nested template I have a string generated from an array. A simplified model of my html:
<template name="parent">
<a class="logId">Console log parent Id</a>
{{> firstChild}}
{{> secondChild}}
</template>
In addition my child templates have an each loop like this:
<template name="firstChild">
{{#each users}}
{{this}}
{{> adminSelectUser}}
{{/each}}
</template>
<template name="adminSelectUser">
<a class="selectUser">SelectUser</a>
</template>
In my js code:
Template.parent.events({
'click .logId': function () {
var eventId = this._id._str;
console.log(eventId);
});
Template.adminSelectUser.events({
'click .selectUser': function(template){
var userString = this.valueOf();
console.log(userString);
}
});
To this point I get it to work and in both of my templates I can find the id of my parent and the string of my user as I want it to do. But then I would also want to use both my variables eventId and userString in the same method call, called from the template adminSelectUser. Is there any way that I can pass the eventId found in the parent template down to the adminSelectUser template so that I can work with both variables with the same method call.
This is what I aim to do with my server methods:
Meteor.methods({
removeUser: function(){
Meets.update({_id: eventId}, {$pull : {usersApplied: userString}});
}
});
I want my server method to recognize the eventId and the userString and get them to work together when I call the method later on in the adminSelectUser template.
Hope this is clear enough!

There are several ways to go about this.
Using Template.parentData
You can traverse the template hierarchy from within template JS using Template.parentData(n) where n is the number of levels up in the hierarchy you want to go. So specifically for your example:
Template.adminSelectUser.events({
'click .selectUser': function(template){
var user = this;
var parentId = Template.parentData(2)._id;
myRemoveMethod(parentId, user);
}
});
However, this means that the inner template relies on always being rendered within the same hierarchy, which kinda has a bad smell. If the templates are tightly coupled and this is the case (which is likely) then using parentData is probably fine. However, if a template has data dependencies it does make sense to pass the appropriate data explicitly (see #2).
Pass the data into the templates explicitly.
<template name="firstChild">
{{#each users}}
{{this}}
{{> adminSelectUser user=this parent=..}}
{{/each}}
</template>
In your example, when you pass no argument to a template render, the data context is assumed to be this. Here instead we pass a user and a parent into the template (Note the use of .. here to reference this outside of the users loop). Your handler would then look more like:
Template.adminSelectUser.events({
'click .selectUser': function() {
var data = Template.currentData();
myRemoveMethod(data.parent._id, data.user);
}
});

Related

filtering variables in meteor/blaze template

Suppose I have a Meteor template that I want to use inside of other Meteor templates.
<template name="helpsout">
<p><b>{{text}}</b></p>
</template>
Suppose I want to call this helper from another template needshelp that gets an array of strings arr as a helper, and I want to invoke the helpsout template on each element of arr but modifying it first say by prepending "this needs help: ". I'd like to write something like this:
<template name="needshelp">
{{#each arr}}
{{> helpsout text="this needs help: {{this}}"}}
{{/each}}
</template>
But the {{this}} is not interpolated and it ends up setting text to the literal "this needs help: {{this}}".
Is there a way to do this without copying the contents of helpsout directly into needshelp? (You can imagine that helpsout is actually a complex template that's used by several other templates so we wouldn't want to copy it into each of the places it's being used.) It seems like having subexpressions would do it but AFAIK this isn't currently supported in Meteor.
You have two choices:
prefix is common
If it's a common pattern in your app that helpsout should be called with some sort of body text along with some prefix text, I would modify the context of helpsout so that it takes a body and an optional prefix like so:
<template name="needshelp">
{{#each arr}}
{{> helpsout prefix="this needs help: " body=this}}
{{/each}}
</template>
Template.helpsout.helpers({
text: function() {
return (this.prefix || '') + this.body;
}
});
prefix is uncommon
If you'd prefer to keep your code as it is with no changes to helpsout then you can use an additional helper in your needshelp template to set the context:
<template name="needshelp">
{{#each arr}}
{{> helpsout text=helpText}}
{{/each}}
</template>
Template.needshelp.helpers({
helpText: function() {
return "this needs help: " + this;
}
});

Meteor each loop within template

So I'm looking for some understanding on how templates and such work.
I've movie.html file with a template named movies in it, with a movie.js helper that returns a collection.
Movies Template
<template name="movies">
<li>{{title}}</li>
</template>
JS Helpers
Template.body.helpers({
movie: function () {
return Movies.find({});
}
});
Now I've another template that does a bunch of other things but one of them is to iterate over this list and display it.
List Template
<template name="list">
<ul>
{{#each movie}}
{{> movies}}
{{/each}}
</ul>
</template>
In this situation the list doesn't popular with the data.
However, if I move the contents of the list template outside of a template and just on the main.html it works great!
This is how I used to use it but I've started to use Houston Admin Package which uses Iron:Router so I've moved the main (and only) page to a template for routing purposes, which breaks my looping list.
I'm sure I'm missing something minor but I can't figure it out.
You are using the {{#each movie}} helper on the list Template so change the Template.helper to the list template
Template.list.helpers({
movie: function () {
return Movies.find({});
}
});
We are you calling this <template name="list"> on the body tag? you have something like this.
<body>
{{> list}}
</body>
Or you have something like this.
<template name="layout">
{{> yield}} <!-- since you are mentioning iron:route package -->
</template>
Or you have a route to that list template? localhost:3000/lists
Router.route('/movie-lists', function () {
this.render('lists')
});
On whatever of this 3 cases, you should point the helper into the template where you are calling it (in this case list template)

Get contents of Meteor template

I have the following Meteor template:
<template name="homeBoxTpl">
<div>
Some content
</div>
</template>
I have an event binding so that when a button on the page is clicked it should get the html contents of the template "homeBoxTpl" - how can this be achieved?
What you need is an event handler, and like Chase say you can access the content of meteor templates using jquery, but meteor has its own way. In order to get a copy of the html, you can place a wrapper round the content in the template, then do this:
Template.homeBoxTpl.events({
'click #someButton':function(e,t){
var templateContents = t.$('.wrapperInTemplate').html();
}
})
Here we are using Template.$ wich returns a jQuery object of those same elements.
Take a look into Template.instances for more information
Just to clarify, where is the button? Is it in another template? I assume you only render homeBoxTpl once.
In which case the event handler for the template where your button exists will have no reference to another template instance. There is no global lookup where you can find all rendered instances of a specific Template
You will have to set an unique identifier "id" for it, or some other discerning info such as a class/attribute and find it via old fashioned JS DOM selectors/traversal.
document.getElementById is fastest, but if there are multiple instances of that template, t.firstNode does give you a good starting point for the DOM traversal.
However making your code dependent on a specific DOM layout is bad practice / too much coupling. Is there any reason why the data underlying that Template's HTML content isn't available somewhere else like a session or collection? It would perhaps be more flexible too to access the data not the HTML.
You can use jquery to access what you need. For example, if you have the following template:
<template name="homeBoxTpl">
<div class="content-container">
Some content
</div>
<button id="btn" type="button">Click me</button>
</template>
Then use the following javascript:
Template.homeBoxTpl.events({
'click #btn': function(e) {
e.preventDefault();
var content = $(".content-container").text();
console.log(content); //Result is "Some content"
}
});

How to know when a handlebars template has finished rendering/rerendering

I have a handlebars template to display a table.
The content changes on a drop down select which makes an ajax call to get data for the template. Now I need to add a CSS class to a table row dynamically after the data gets fetched. Now I have to add a timeout of 100 ms so the selector works.
Because at the point I am setting the class the first time the table tr does not exist. How do I know if the handlebars template is done rendering the new data ( first time ) so I do not have to rely on timeOut function. This is the controller function that sets data for the template and add CSS.
updateData: function(data,index)
{
this.set('data',data);
setTimeout(function() {
console.log('sleep');
Ember.$('.table tr').removeClass("active");
Ember.$('.table tr:eq(' + (index+1) + ')').addClass("active");
}, 100);
}
This is the type of problem that is best solved using an Ember.View, it will make things easier.
You can create a view for each row in your table and in each view you can use the classNameBindings property that will add or remove any number of CSS classes according to the property they are bound to.
See: http://emberjs.com/api/classes/Ember.View.html
In your template you would have something like:
<table>
{{#each item in model}}
{{view 'my-row' ...}}
{{/each}}
</table>
And in your view you would have something like:
Ember.View.extend({
tagName: 'tr',
classNameBindings: ['active']
})
You can use the Ember.run.scheduleOnce method to schedule the CSS class manipulation to occur after the afterRender queue is emptied (i.e. the dom is fully rendered)
Click here for more information about the Ember Run Loop
So in your example,
updateData: function(data,index) {
this.set('data',data);
Ember.run.scheduleOnce('afterRender', this, function() {
console.log('sleep');
Ember.$('.table tr').removeClass("active");
Ember.$('.table tr:eq(' + (index+1) + ')').addClass("active");
});
}

Context inside templates with Iron-Router

I'm having trouble understanding exactly what is available as my context in a template invoked by Meteor with Iron-Router – and how these things inherit.
Here are all the potential sources for "stuff I can refer to inside double curly braces" that I can think of:
Built-in helpers
Handlebars.registerHelper(...)
Template.myTemplate.myVar/myHelper = ...
Template.myTemplate.helpers({ ... })
data: { ... } inside route (Router.map)
Something to do with #each?
Something to do with #with?
Have I forgotten anything? Are there any global variables?
I guess I'm a bit confused about what the standard way is of giving the template a context in the first place. Also about what happens inside control structures such as #each and #with.
A clarification would be great!
IronRouter renders your template with the result of RouteController.data as the current data context.
<template name="viewPost">
<div>
<h1>{{title}}</h1>
<p>{{content}}</p>
</div>
</template>
var PostsController=RouteController.extend({
template:"viewPost",
waitOn:function(){
return Meteor.subscribe("postsById",this.params._id);
},
data:function(){
return Posts.findOne(this.params._id);
}
});
this.route("viewPost",{
path:"/posts/:_id",
controller:PostsController
});
In this example, IronRouter will render the "viewPost" template with the post having this.params._id as data context.
What is the standard way of giving a template a context in the first place ?
There is 2 ways :
{{#with myContext}}
{{> myTemplate}}
{{/with}}
{{> myTemplate myContext}}
As you can see, the #with control structure sets the current data context.
The #each structure iterates over a Cursor (or an Array) and sets the current data context to the current fetched document (or the current cell).
<template name="postsList">
{{#each posts}}
<h1>{{title}}</h1>
{{/each}}
</template>
Template.postsList.helpers({
posts:function(){
// cursor
return Posts.find();
// array
return [{title:"Title 1"},{title:"Title 2"},{title:"Title 3"}];
}
});
UPDATE : Could you possibly add a note about inheritance ? For instance, if I have nested #each blocks, do variables cascade ?
I came up with this example :
<template name="parent">
<ul>
{{#each parentContexts}}
{{> child}}
{{/each}}
</ul>
</template>
<template name="child">
<li>
<ul>
{{#each childContexts}}
{{> content}}
<p>../this.parentProperty = {{../this.parentProperty}}</p>
{{/each}}
</ul>
</li>
</template>
<template name="content">
<li>
<p>this.childProperty = {{this.childProperty}}</p>
<p>this.parentProperty = {{this.parentProperty}}</p>
</li>
</template>
Template.parent.helpers({
parentContexts:function(){
return [{
parentProperty:"parent 1"
},{
parentProperty:"parent 2"
}];
}
});
Template.child.helpers({
childContexts:function(){
return [{
childProperty:"child 1"
},{
childProperty:"child 2"
}];
}
});
If you run this example, you'll notice that you can't access the parentProperty in "content" because the default #each helper OVERRIDES the parent data context with the new context provided.
You can access the parentProperty in the nested #each block using this syntax : ../this.parentProperty, which is reminiscent of the UNIX parent directory access syntax.
However you cannot use this syntax in the "content" template, because it is agnostic of the nested each structures it was called from : you can only use the ../../parent syntax in a template where the actual nesting happens.
If we want to access the parentPropery in the content template, we must augment the current data context with the parent context.
To do so, we can register a new #eachWithParent helper like this :
Handlebars.registerHelper("eachWithParent",function(context,options){
var parentContext=this;
var contents="";
_.each(context,function(item){
var augmentedContext=_.extend(item,parentContext);
contents+=options.fn(augmentedContext);
});
return contents;
});
Now if you replace the nested #each with this new helper, you will have access to parentProperty in "content".