Why does nested each in template output nothing - javascript

While this is rendered as expected:
{{#each Items}} // a collection
{{title}}
{{/each}}
{{#each types}} // another ollection
{{refId}}
{{/each}}
if I put it together:
{{#each Items}}
{{title}}
{{#each types}}
{{refId}}
{{/each}}
{{/each}}
The #each types is empty.
The template helpers:
Template.menuItems.helpers({
restMenuItems: function() {
return RestMenuItems.find({restRefId: this._id}, {sort:Template.instance().sort.get()});
},
restMenuSideItems: function() {
return RestMenuSideItems.find({restRefId: this._id}, {sort:Template.instance().sort.get()});
}
});
Template.menuItems.onCreated( function(){
this.subscribe('restMenuItems');
this.subscribe('restMenuSideItems');
});
And some of the template code:
{{#each restMenuItems}}
<div>
<select class="list-group select_side_addons">
{{#each restMenuSideItems}}
<option>{{restRefId}}</option>
{{/each}}
</select>
</div>
{{/each}}
Even when replacing {{#each restMenuSideItems}} by {{#each ../restMenuSideItems}}, nothing appears.
What's wrong?

Because the #each clause changes the data context to the current item.
If you want a list of types inside each of the Items, you can get the parent data context using ...
If types is an attribute of the parent context:
{{#each Items}}
{{title}}
{{#each ../types}}
{{refId}}
{{/each}}
{{/each}}
If types is a template helper, you can pass the parent context as an argument to it:
{{#each Items}}
{{title}}
{{#each types ..}}
{{refId}}
{{/each}}
{{/each}}
and use the context for your query.
Template.myTemplate.helpers({
types: function(parent) {
// you now have the parent context here.
// use parent._id etc. where you used this._id in the
// "flat" version.
}
});

Related

Meteor: Using variable in child template

I'm using template-level subscriptions, but I have some problems to use the results in a child template:
As I'm loading the template example, the template exampleChild will be displayed (just take this as an basic example - I know this doesn't make sense right now).
On creating the main template, the subscription is done and the data gets stored in a helper:
Template
<template name="example">
{{> Template.dynamic template=switch}}
</template>
<template name="exampleChild">
<h1>{{result}}</h1>
</template>
Helper
Template.example.helpers({
switch: function() { return 'exampleChild'; },
result: function() { return Template.instance().result(); },
});
Event
Template.example.onCreated(function() {
var instance = this;
instance.autorun(function () {
var subscription = instance.subscribe('posts');
});
instance.result = function() {
return Collection.findOne({ _id: Session.get('id') });
}
});
If I would put the {{result}} in the example-Template, everything is working. But I need to use the variable in the child template.
Did you try:
<template name="example">
{{#with result}}
{{> Template.dynamic template=switch}}
{{/with}}
</template>
<template name="exampleChild">
<h1>{{this}}</h1>
</template>
With doc
When doing that, I prefer wrap the data context result into an object, to have something proper on the child like:
Template.example.helpers({
switch: function() { return 'exampleChild'; },
data: function() { return {result: Template.instance().result()} },
});
<template name="example">
{{#with data}}
{{> Template.dynamic template=switch}}
{{/with}}
</template>
<template name="exampleChild">
<h1>{{result}}</h1>
</template>
Hope it's help
You could store the result in a session variable, and then add another helper to exampleChild that waits for the session variable.

meteor template: parse html content

I saved some HTML-content in my collection. By using IronRouter data() will be used to set the cursor/array. In this example I just show an example array.
Now the HTML-content isn't shown correctly, as it doesn't get parsed. The user would see the HTML-tags. What do I have to do to get the content displayed correctly?
IronRouter
Router.route('/article/:_id', {
name: 'article',
data: function () {
var articles = [{ title: 'title', content: '<strong>content</strong>'}];
return { articles: articles };
}
});
template
<template name="article">
{{#each articles}}
<h1>{{title}}</h1>
<section>{{content}}</section>
{{/each}}
</template>
Use Handlebars triple-stash:
<template name="article">
{{#each articles}}
<h1>{{title}}</h1>
<section>{{{description}}}</section>
{{/each}}
</template>

Ember use separate child template files

I have the below template file (actually used for my component)
{{#each details.secs as |sec|}}
<div class="row">
{{#each secs.flds as |fld|}}
// if fld.id is 'abc', use abc.hbs
// if fld.id is 'xyz', use xyz.hbs
{{/each}}
</div>
{{/each}}
My question is how can I use separate sub-template files and include them in the above parent file (based on condition)
Thus if field.id is 'abc', it should use rendering logic from abc.hbs
Also abc.hbs would need the 'field' model input for rendering purpose (It's output should get appended to the main template)
With Ember 2.0, creating and registering our own eq (equals) helper is super easy:
ember generate helper eq
Include our equality function:
// app/helpers/eq.js
import Ember from 'ember';
export function eq(params, hash) {
return (params[0] === params[1]);
}
export default Ember.Helper.helper(eq);
And use it!
{{#each details.sections as |section|}}
<div class="row">
{{#each section.fields as |field|}}
{{#if (eq field.id "abc")}}
{{abc-component data=field}}
{{/if}}
 {{#if eq field.id "xyz)}}
{{xyz-component data=field}}
{{/if}}
{{/each}}
</div>
{{/each}}

Ember CLI- conditional output within an each loop using a component instead of itemController

In my Ember CLI app, I'm using {{#each}} helpers to output the rows of a table. 'name' 'created_date' and 'type' are all defined in the related model.
{{#each model as |job|}}
<tr>
<td>{{job.name}}</td>
<td>{{job.created_date}}</td>
<td>{{job.type}}</td>
<td>
{{#if typeZero}}
<p>Content that will display if the value of 'type' is 0.</p>
{{/if}}
</td>
</tr>
{{/each}}
In the fourth table cell of each row, I'd like to display certain content if that value of 'type' for that record is 0.
I first tried adding an itemController to the each helper:
{{#each job in model itemController="jobrowcontroller"}}
......
{{/each}}
This gave me an error: "Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed ***#controller:array:, but it should have been an ArrayController"
I found that itemController is now deprecated, and components should be used instead.
I created a component named job-table-row, and updated the page template:
{{#each model as |job|}}
{{#job-table-row model=job as |jobTableRow|}}
<tr>
<td>{{job.name}}</td>
<td>{{job.created_date}}</td>
<td>{{job.type}}</td>
<td>
{{#if typeZero}}
<p>Content that will display if the value of 'type' is 0.</p>
{{/if}}
</td>
</tr>
{{/job-table-row}}
{{/each}}
In the component handlebars file, I simply use {{yield}} and everything displays fine.
In the component js file, I have:
import Ember from 'ember';
export default Ember.Component.extend({
tagName: '',
typeZero: function() {
var currentStatus = this.get('model.status');
if (currentStatus === 0) {
this.set('typeZero', true);
} else this.set('typeZero', false);
}.on('didInsertElement'),
});
The problem with this is that the function 'typeZero' doesn't run. Is it possible to achieve this with a component, or do I need to use a different method altogether?
You cannot yield because typeZero only exists inside the component. Instead, move the template to the component:
// templates/components/job-table-row.hbs
<td>{{model.name}}</td>
<td>{{model.created_date}}</td>
<td>{{model.type}}</td>
<td>
{{#if statusZero}}
<p>Content that will display if the value of 'status' is 0.</p>
{{/if}}
</td>
And simplify your template outside:
<table>
<tbody>
{{#each model as |job|}}
{{job-table-row model=job}}
{{/each}}
</tbody>
</table>
Also, you can replace your complex method with a computed property:
// components/job-table-row.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'tr',
statusZero: Ember.computed.equal('model.status', 0)
});
See it all working at http://ember-twiddle.com/de8a41b497ef4f116bab

How to access another collection by ID in Meteor template?

Let's say I have a Posts collection, the data for which looks like this:
var post1 = {
title: "First post",
post: "Post content",
comments: [<comment id>, <another comment id>, <etc>]
};
And I have a corresponding Comments collection. I've published and subscribed to both collections and want to display a post with it's comments.
How do I go about displaying the comments for that specific post?
<template name="post">
<h1>{{title}}</h1>
<p>{{post}}</p>
{{#each comments}}
// Only have access to the ID
{{/each}}
</template>
I could create a helper like this:
Template.post.helpers({
displayComment: function(id) {
return Comments.findOne(id).fetch().comment;
}
});
and do:
<template name="post">
<h1>{{title}}</h1>
<p>{{post}}</p>
{{#each comments}}
{{displayComment #index}}
{{/each}}
</template>
But then I'd have to create a helper for each of the comment object's properties, etc.
What's the clean way to do it? I don't want to populate the comments field on the post object, since that would mean calling .fetch(), and posts would no longer be reactive.
A couple of suggestions:
<template name="post">
<h1>{{title}}</h1>
<p>{{post}}</p>
{{#each comments}}
{{#with commentDetails}}
{{userName}} //
{{content}} // These are properties of a comment document
{{upvotes}} //
{{/with}}
{{/each}}
</template>
Template.post.helpers({
commentDetails: function() {
return Comments.findOne(this);
}
});
That will set the data context within each with block to be the comment object returned by looking up the current _id (which is the value of this in the commentDetails helper within the each block).
An alternative would be to just use an each block on its own, but have it iterate over a cursor:
<template name="post">
<h1>{{title}}</h1>
<p>{{post}}</p>
{{#each commentCursor}}
{{content}}
... // other properties
{{/each}}
</template>
Template.post.helpers({
commentCursor: function() {
return Comments.find({_id: {$in: this.comments}});
}
});

Categories