Dynamically load partials in single Ractive instance - ractivejs

I would like to make a template that can determine which partial it will render, such that
In Template:
{{#objectA}}
{{>partial}}
{{/}}
{{objectB}}
{{>partial}}
{{/}}
Where partial is a property with template value on objectA and objectB
Is there any reasonable way of doing this, or something similar in a single instance?

Checkout Partial Expressions aka dynamic partials they do exactly that based on resolution of the partial name as a reference. Assuming each object had a foo property:
{{#partial bar}}
bar partial!
{{/partial}}
{{#partial qux}}
qux partial!
{{/partial}}
{{#each items}}
<li>item {{>foo}}
{{/each}}
And data was like:
items: [
{ foo: 'bar' },
{ foo: 'qux' },
]
see http://jsfiddle.net/52k645wh/

Related

loop through an array of object with handlebars.js

I have a structure like this:
obj: {
array: [{
string: 'hello'
},
string: 'tim'
}]
}
how can i print all the string propery in an unordered list?
i'm trying with this but it doesn't print nothing
<ul>
{{#each array}}
<li>{{this string}}</li>
{{/each}}
</ul>
Try this code
<ul>
{{#each array}}
<li>{{this.string}}</li>
{{/each}}
</ul>
TL;DR: According to each helper code in the docs, context is set to current item.
Second argument implicitly passed to helper is options object, that contains fn property. This property is a function that behaves like a normal compiled Handlebars template (containing what have been placed between {{#each}} and {{/each}}).
Helper iterates over the array and evaluate the template for each item. Concatenated result is returned.
So, you don't have to use this.
<ul>
{{#each array}}
<li>{{string}}</li>
{{/each}}
</ul>
Fiddle Example

Dynamic paths in handlebars

I searched through a lot of questions but hasn't found answer.
I use handlebars templates and have data structure:
{
privileged_users: [ "user1", "user2" ],
users: {
user1: { name: "N1" },
user2: { name: "N2" },
user3: { name: "N3" }
}
}
I wan't to output all privileged users with some template. Something like this:
<table>
{{#each privileged_users}}
<tr><td>{{../users.[this].name}}</td></tr>
{{/each}}
</table>
Is it possible without additional helpers?
If it isn't how can I write block helper with changing context to ../users.[this] ?
Register following helper:
Handlebars.registerHelper('lookupProp', function (obj, key, prop) {
return obj[key] && obj[key][prop];
});
Then modify the template like:
<table>
{{#each privileged_users}}
<tr><td>{{lookupProp ../users this 'name'}}</td></tr>
{{/each}}
</table>
Here is the working fiddle.
Previous one is just a simple expression helper.
Now here is a working jsfiddle according to question.
Handlebars has a built-in lookup helper since version 3.0.3.
An alternate to block helper could be Handlebars Partial Context approach i.e. define/register a partial and use it with different context in main template.

Handlebars dynamic property lookup in each loop

In Handlebars 2+, how do I dynamically read a property in a loop like this? objects is an array of objects. keys is an array of strings. I want to loop each key for each object and put the its .foo value in the span.
{{#each objects}}
{{#each keys}}
<span>{{../this.{{this}}.foo}}</span>
{{/each}}
{{/each}}
Is this possible in plain Handlebars 2+? Or...is there a helper that does this?
I don't see the way how it can be done without helper.
With helpers everything is possible (but kind of ugly) in Handlebars.
For example, you could use something like this:
{{#each objects}}
{{#each keys}}
<span>{{lookupProp ../this this 'foo'}}</span>
{{/each}}
{{/each}}
And helper:
Handlebars.registerHelper('lookupProp', function (obj, key, prop) {
return obj[key] && obj[key][prop];
});
Look at the fiddle.
Handlebars has built-in lookup helper since version 3.0.3.
Okay... spent a few hours googling around and find a nice solution, as I had the same issue, but failed to find any...
I was as happy as Larry and jumped off my chair when I finally figured a way to get this working :D
This way, you can access object values with dynamic keys,
Demo object:
var categories = {
onion: { name: 'bar', id: 4 },
apple: { name: 'demo', id: 2 },
carrot: { name: 'foo', id: 3 },
Root: [
{ name: 'apple' },
{ name: 'onion' },
{ name: 'carrot' }
]
};
Instead of trying something like these: (which won't work)
{{#each categories.[#key]}}
or
{{#each categories.[mykey]}}
You can do:
{{#each categories.[Root] as |main-category|}}
{{#each (lookup ../categories main-category.name) as |sub-category|}}
{{sub-category.name}}
{{/each}}
{{/each}}
Hope it will help for someone else too :)
For anyone else that doesn't want to loop you could use with eg.
{{#with (lookup myObject myKeyVar) as |subObject|}}
{{subObject.key}}
{{/with}}

How do I get templates inserted from custom block helpers to be individually rerendered in Meteor?

When I use the built-in block helper #each, book templates are rerendered individually when changed:
users =
_id: 'foo'
books: [
{name: 'book1'}
{name: 'book2'}
]
<template name="user">
{{#each books}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div>{{name}}</div>
</template>
When the data is changed - the first book name is set to 'bookone' instead of 'book1' - only the book template (the div containing 'book1') is rerendered. This is the desired behavior. When I use a custom block helper, the behavior is different:
<template name="user">
{{#each_with_id}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div data-id="{{_id}}">{{name}}</div>
</template>
Templates.user.each_with_id = (options) ->
html = "
for book, i in this.books
this.name = book.name
html += Spark.labelBranch i.toString(), ->
options.fn this
html
Now when the name of the first book changes, the whole user template is rerendered.
It does not work as you expect, because the implementation of built-in each is based on the cursor.observeChanges feature. You will not be able to achieve the same exact result without using an auxiliary collection of some sort. The idea is quite simple. It seems that you don't have a "books" collection but you can create a client-side-only cache:
Books = new Meteor.Collection(null);
where you will need to put some data dynamically like this:
Users.find({/* probably some filtering here */}).observeCanges({
added: function (id, fields) {
_.each(fields.books, function (book, index) {
Books.insert(_.extend(book, {
owner: id,
index: index,
}));
}
},
changed: function (id, fields) {
Books.remove({
owner:id, name:{
$nin:_.pluck(fields.books, 'name')
},
});
_.each(fields.books, function (book, index) {
Books.update({
owner : id,
name : book.name,
}, {$set:_.extend(book, {
owner : id,
index : index,
})}, {upsert:true});
}
},
removed: function (id) {
Books.remove({owner:id});
},
});
Then instead of each_with_id you will be able to the built-in each with appropriate cursor, e.g.
Books.find({owner:Session.get('currentUserId')}, {sort:{index:1}});
You may also look at this other topic which basically covers the same problem you're asking about.

Meteor template: Pass a parameter into each sub template, and retrieve it in the sub-template helper

I am trying to figure out how to pass a parameter into a sub-template that is in an each block and use the parameter in the sub-template as well as sub-template helper. Here is what I tried so far:
template:
<template name="parent">
{{#each nodes }}
{{> child myParam}}
{{/each}}
</template>
<template name="child">
{{ paramName }}
</template>
js:
Template.parent.nodes = function() {
//return a list
};
Template.parent.myParam = function() {
return {"paramName" : "paramValue"};
};
Template.child.someOtherHelper = function() {
//How do I get access to the "paramName" parameter?
}
So far, it hasn't been working, and it seems somehow mess up my input node list also.
Thanks for help.
When you use {{> child myParam}}, it's calling the child template and associates myParam as current template data context, meaning that in the template you can reference {{paramName}}.
In someOtherHelper you could use this.paramName to retrieve "paramValue".
However, when you're using {{#each nodes}}{{> child}}{{/each}}, it means that you pass the content of the current list item (fetched from a LocalCursor or directly an array item) as the template data of child, and you can reference the list item properties using {{field}} in html or this.field in js.
What's happening here is when you call {{> child myParam}}, the myParam helper content OVERWRITES the current node item as template data, that's why it's messing your node list.
A quick (dirty) trick would be to simply extend the myParam helper so that it also contains the template data from the {{#each}} block.
Template.parent.helpers({
nodes:function(){
// simulate typical collection cursor fetch result
return [{_id:"A"},{_id:"B"},{_id:"C"}];
},
myParam:function(){
// here, this equals the current node item
// so we _.extend our param with it
return _.extend({paramName:"paramValue"},this);
}
});
Template.child.helpers({
someOtherHelper:function(){
return "_id : "+this._id+" ; paramName : "+this.paramName;
}
});
<template name="parent">
{{#each nodes}}
{{> child myParam}}
{{/each}}
</template>
<template name="child">
{{! this is going to output the same stuff}}
<div>_id : {{_id}} ; paramName : {{paramName}}</div>
<div>{{someOtherHelper}}</div>
</template>
Depending on what you're precisely trying to achieve, there might be a better approach but this one gets the job done at least.

Resources