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)
Related
Is there any way to dynamically load a template with onCreated method of Meteor.js? I have different template and one display area (main template).
<template name="main">
</template>
default loaded template
<template name="default">
</template>
Loaded templates via links
<template name="page1">
</template>
<template name="page2">
</template>
Is there a way I can use oncreated function to load the default, and remove (default) and load other template in the same main template when they are clicked?
Check out Template.dynamic.
It allows you to load a template only by its name. Easy thing here because you can let your template handle dynamic names by using a Reactive var or Reactive Dict.
Declare some templates:
<template name="main">
{{> Template.dynamic template=getTemplateName }}
<button class="loadTemplate" data-target="page1">Load Page 1</button>
<button class="loadTemplate" data-target="page2">Load Page 2</button>
</template>
<template name="page1">
</template>
<template name="page2">
</template>
<template name="default">
</template>
In your main template you can set in onCreated the default template name default:
Template.main.onCreated(function(){
this.state = new ReactiveDict();
this.state.set("targetTemplate", "default");
})
Get the template to load via helper:
Template.main.helpers({
getTemplateName() {
return Template.instance().state.get("targetTemplate");
},
})
And set the new template name by button click event:
Template.main.events({
'click .loadTemplate'(event, templateInstance) {
event.preventDefault();
const targetName = $(event.currentTarget).attr('data-target');
templateInstance.state.set("targetTemplate", targetName);
}
})
You can of course do this also by other events than only by clicking a button, since it depends on a ReactiveDict / ReactiveVar it will render the new template if the variable updates.
Note: Very important is to handle the data that is passed to the template, too. Once your dynamic template becomes more complex you have to be more aware of that, too.
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);
}
});
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;
}
});
I'm trying to add an attribute to many external links. The below code worked prior to Blaze, which only runs Template.rendered once now (but the below code doesn't run as desired one time even).
So what is an alternative way to add an attribute to many links once the page has been rendered or what is the correct way to do this with Meteor now? BTW, I researched quite a few things, including this example app from the author, which if it has the answer, I didn't see it.
Template.layout.rendered = function () {
console.log('CALLED'); // runs
$(document).ready(function () {
console.log('NOW THIS'); // runs
$('a.external').each(function () {
console.log('NOT CALLED'); // doesn't run
$(this).attr('target', '_blank');
});
});
};
There are several ways to deal with this problem, none of them is perfect. It's worth noting that this issue has been raised and most likely it will be solved via a custom event as proposed here.
For now, the simplest way to do this is to move each element you need to adjust to a separate template. So instead of:
<template name="layout">
{{#each links}}
<a class="external" ...>
{{/each}}
</template>
You will have:
<template name="layout">
{{#each links}}
{{> layout_externalLink}}
{{/each}}
</template>
<template name="layout_externalLink">
<a class="external" ...>
</template>
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".