Accessing another variable with contents of each loop - handlebars.js

The data being passed into the handlebars template looks like this:
a = {
x: {
name: "xavier"
},
y: {
name: "yamal"
},
}
b = {
properties: {
x: {
property: "number"
}
}
}
Handlebars template looks like this:
<div class="left-panel">
{{a.x.name}} // Prints correctly "xavier"
{{#each b.properties}}
<h4>{{#key}}</h4> // Prints correctly "x"
<h4>{{ ../a.[#key].name }}</h4> // does not print "xavier"
{{/each}}
</div>
As you can see, I want to be able to access the name of a dict in a using the key in b. How can I achieve this?

Your question is essentially the same as this one: Handlebars.js - Access object value with a variable key
The only extra detail is that you will need to make use of Handlebars subexpressions in order to perform the two lookups (first of #key; then of 'name').
A subexpression will allow you to lookup the value of #key on ../a and pass that to a second lookup of the value of 'name' on the result of the first lookup.
The relevant line in your template becomes:
<h4>{{lookup (lookup ../a #key) 'name'}}</h4>
Here is a fiddle for your reference:
https://jsfiddle.net/76484/b0puy52n/

Related

iterate over data from a route in a template

I have a cursor returned from a route, how can I use the data as context in a template?
example:
router.js
this will return a cursor with all documents where parent is equal to the params_id
this.route('my route', {
path: '/myroute:_id',
data: function(){
return MyCollection.find({parent: this.params._id});
}
});
how should my template look like to "iterate" over the cursor? normally if I use MyCollection.find({}) I iterate with #each and give the context a name via a TemplateHelper. I guess
{{#each data}}....{{/each}}
should be right but it doesn´t work.
Setting data in the route sets the context for the template. In the template, the context is accessed via this:
{{#each this}}...{{/each}}
Alternatively, if you prefer to assign a name to your data, can return an object from the route:
data: function(){
return {posts: Posts.find({parent: this.params._id})};
}
And then you can iterate over the documents like so:
{{#each posts}}...{{/each}}

Display error with using firebase-element inside template repeat

<template repeat="memberId in members | objKeys">
<firebase-element data={{member}} location="{{'SOME_LOCATION/' + memberId}}"></firebase- element>
<h2>member.name</h2>
</template>
objKeys: function(members) {
return Object.keys(members);
}
the data looks like this
members = {
'memberId_1': true,
'memberId_2': true,
'memberId_3': true
}
and at another location store actual users data.
Here, I'm expecting the template repeat to render each user (member1, member2, member3) accordingly. However, it prints out same name for 3 entries as if it re-use the variable "member" for all 3 firebase element in the template repeat, which doesn't really make sense.
I've tried to modified objKeys functions to return
[ {memberId: memberId_1, member: {}},
{memberId: memberId_2, member: {}},
{memberId: memberId_3, member: {}]
then use the inner member object for firebase element but the result is still the same
<template repeat="{{item in members | objKeys}}>
<firebase-element data={{item.member}} location={{'SOME_LOCATION/' + item.memberId}}> </firebase-element>
</template>
Do I not understand template repeat correct and use it incorrectly here ? Or is it a bug with polymer template.
I think the problem is that the repeater binds {{member}} to itself.
Check your Firebase - you'll see that the binding not only displays the name name repeatedly in your view, it also set all the values to the same name in your Firebase.
Try this instead, using {{members[memberId}}:
<polymer-element name="member-test">
<template>
<firebase-element data="{{members}}" location="{{'https://YOUR_APP_NAME.firebaseio.com/members/'}}"></firebase-element>
<template repeat="{{memberId in members | objKeys}}">
<firebase-element data="{{members[memberId]}}" location="{{'https://YOUR_APP_NAME.firebaseio.com/members/' + memberId}}"></firebase-element>
<h2>{{members[memberId].name}}</h2>
</template>
</template>
<script>
Polymer({
objKeys: function(members) {
if(!members) return null;
return Object.keys(members);
}
});
</script>
</polymer-element>
This assumes that your Firebase has data in the following format:
{ members:
{ memberID1: { name: "Name1" },
memberID2: { name: "Name2" }
}
}

Why isn't the URL being generated for this route?

So, I'm working on a Meteor project and I can't get this route to generate properly, or at all for that matter.
<template name="browseAll">
<h3>List of classes with books available!</h3>
<ul>
{{#each aggCount}}
<li>{{ _id }} ({{ count }})</li>
{{/each}}
</ul>
</template>
The data that is being iterated over is a result of aggregation using MongoInternals, and that is as follows:
(server/methods.js excerpt):
classCount: function() {
// Attempt aggregation of the books table to count by class, maybe.
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var col = db.collection("books");
var aggregateSync = Meteor._wrapAsync(col.aggregate.bind(col));
var pipeline = [
{$group: {_id: "$class", count: {$sum: 1}}},
{$sort: {_id: 1}}
];
var theAnswer = aggregateSync(pipeline);
return theAnswer;
}
It seems that the data is coming through okay, and sample data from aggregation (coming into the template) looks like this:
[ { _id: 'ADNR1234', count: 2 }, { _id: 'ARTH1234', count: 1 } ]
That's the template code I've got, and this is the route that it's supposed to be working with:
this.route('browse-class', {
path: '/browse/:_class',
data: function() {
var booksCursor = Books.find({"class": this.params._class},{sort:{"createdAt": 1}});
return {
theClass: this.params._class,
numBooks: booksCursor.count(),
books: booksCursor
};
}
});
I don't understand it. The data is being SHOWN, and what I want to do is generate a URL for browse-class (route) that takes the value of {{ _id }} in the helper as a parameter, so as to generate something like this:
application.org/browse/CLSS
Be aware that {{pathFor}} must be called with a data context properly set :
{{#with class}}
{{pathFor "browse-class"}}
{{/with}}
Optionnaly it is possible to pass the data context as a parameter :
{{pathFor "browse-class" class}}
The data context provided to pathFor is used when generating the route path, if you defined a route path like this :
path: "/browse/:_id"
Then it will use the _id from the class to properly generate a URL.
For the text of the link, I doubt you want to display the _id, your class documents probably include a "label" so you could use this :
{{ label }}

How to get an array value at index using Handlebars.js?

Say I have JSON:
{
userinput: [
{name: "brian", "value": "i like pies"},
{name: "susan", "value": "memes are stupid"}
],
feedback: [
{value: "i also like pies"},
{value: "null"}
]
}
And I'm trying to draw a table like this:
name ..... | input ...... | feedback
-----------|----------------|-----------------
brian | I like pies | I also like pies
susan | mems are stupid| null
And while I recognise that it would be better to have feedback as a value of "userinput", what I have is not done like that ...
I'm trying to get the index of feedback inside {{#each userinput}}`, e.g.
{{#each userinput}}
<td>{{name}}</td><td>{{value}}</td><td>{{../feedback[#index].value}}</td>
{{/each}}
But of course {{../feedback[#index].value}} does not work.
What is the best way (without changing the structure of the json) to grab the value of the matching index inside the feedback array?
This can be accomplished using the lookup helper:
The lookup helper allows for dynamic parameter resolution using Handlebars variables. This is useful for resolving values for array indexes.
So the template for your example would look like this:
{{#each userinput}}
<td>{{name}}</td>
<td>{{value}}</td>
<td>
{{#with (lookup ../feedback #index)}}
{{value}}
{{/with}}
</td>
{{/each}}
I guess you will have to write a block helper for this, as it seems #index can only be used as a stand-alone.
I modified the "list" example, to allow a template like this: "{{#list userinput feedback}}<td>{{name}}</td><td>{{value}}</td><td>{{#feedback.value}}</td>{{/list}}". The implementation is like this, accepting two parameters "input" and "feedback" (plus the standard "options").
Handlebars.registerHelper('list', function(input, feedback, options) {
var out = "", data;
// iterate over the input
for (var i=0; i<input.length; i++) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
// add "feedback" item to the current frame's data
data.feedback = feedback[i];
}
out += "<tr>" + options.fn(input[i], { data: data }) + "</tr>";
}
return out;
});
Here's the Fiddle.

Handlebars array access with dynamic index

How can I access to an array element inside handlebars template using a variable instead of an hardcoded value?
I need to do something like:
{{#each condition in conditions}}
{{App.ops.[condition.op].name}}
{{/each}}
at the moment doesn't give me a parse error but on runtime doesn't return me nothing.
If i do something like this:
{{App.ops.[1].name}}
it works but it's not what i'm looking for
Related to my answer on another question
You can use the built-in lookup helper:
The lookup helper allows for dynamic parameter resolution using Handlebars variables. This is useful for resolving values for array indexes.
Using lookup, your example could be written as
{{#each condition in conditions}}
{{#with (lookup ../App.ops condition.op)}}
{{name}}
{{/with}}
{{/each}}
(Note that without knowledge of the structure of your data, I'm making an assumption about the location of App.ops.)
You can write a simple helper just to get value from array
Handlebars.registerHelper('getmyvalue', function(outer, inner) {
return outer[inner.label];
});
and then use it in template like
{{#each outer}}
{{#each ../inner}}
{{getmyvalue ../this this }}
{{/each}}
../this references to current outer item, and this - to current inner item
Example of data coming to template:
{
outer: {
1: { foo: "foo value" },
2: { bar: "bar value" }
},
inner: {
1: { label: "foo" },
2: { label: "bar" }
}
}
You need to create a helper for your problem. Below is the sample solution to your problem using index values. If you want to use some conditions you can also do that.
Handlebars.registerHelper("each_with_index", function(array, options) {
if(array != undefined && array != null && array.length > 0){
var html = new StringBuffer();
for (var i = 0, j = array.length; i < j; i++) {
var item = array[i];
item.index = i+1;
// show the inside of the block
html.append(options.fn(item));
}
// return the finished buffer
return html.toString();
}
return "";
});
Then you can do something like this
{{#each_with_index condition in conditions}}
{{App.ops.[condition.index].name}}
{{/each_with_index}}

Resources