Handlebars If Statement not behaving as expected - handlebars.js

I have the following json object -
{
"type": "typeOne",
"Children": [
{
"ChildType": "ChildTypeOne",
"Settings": {
"IsChildTypeOne": true
}
},
{
"ChildType": "ChildTypeTwo",
"Settings": {
"IsChildTypeTwo": true
}
}
]
}
My handlebars template contains the following snippet -
{{#each Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!
{{else}}
ChildTypeTwo!!
{{/if}}
{{/each}}
If I run this data through the template, the only thing that ever renders is ChildTypeTwo!!. So it seems that the if statement isn't properly evaluating IsChildTypeOne. The strange part is that if I put a statement in to display the value of IsChildTypeOne in the else clause, the value is displayed as true for the first ChildType.
Does anyone have any thoughts as to why this is not working as expected?
NOTE - the json posted above is a trimmed down version of my actual object. The real object has nested Children arrays that reuse the same object structure. So for instance, in my example, ChildTypeOne can also have a Childrens array with other objects within it.
EDIT****
So in stepping through the code, I found that if I had my type defined as follows -
...
"Settings" : {
"IsChildTypeOne": 'true'
}
...
it appears to work. Removing the single quoted causes the value to be read as undefined when stepping through.

Given charrs answer didn't seem to help, and the fact that your JSON is more complex than what you've posted, maybe your actual template isn't referencing a parent context correctly? For instance, if you wanted to access the type field in #each children block, it would look like this:
{{#each Children}}
{{#if Settings.IsChildTypeOne}}
{{../type}}
{{/if}}
{{/each}}

This ended up being related to the process being used to serialize the json string into an object. Please see the issue here for an explanation.

Can you try changing the handlebar template code as below:
{{#Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!!
{{else}}
ChildTypeTwo!!!
{{/if}}
{{/Children}}
This would iterate your array of Children and would give you result as
ChildTypeOne!!!
ChildTypeTwo!!!
Since your json has two elements one which has ChildTypeOne true and other not.
Sample handelbar:
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
{{#Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!!
{{else}}
ChildTypeTwo!!!
{{/if}}
{{/Children}}
</div>
</div>
The html Output for above template :
<div class="entry">
<h1>My New Post</h1>
<div class="body">
This is my first post!
ChildTypeOne!!!
ChildTypeTwo!!!
</div>
</div>
You can see ChildTypeOne!!! for first element is coming.

Related

How can I do a string comparison in a Handlebars.Net template?

I am trying to use a Handlebars.Net template to iterate over a list of string and to render either one thing or the other based on the value of the string.
This is a cut-down example of what I am trying to achieve. I have an object with a list of string property, and a template that loops through the list and renders some HTML based on the value of the string:
Simple object with list:
public class Box
{
public List<string> BoxItems { get; set; }
}
Simple template to iterate over the list and render one thing or the other:
{{#each Box.BoxItems }}
{{#if this == "item" }}
<p>This is a box item</p>
{{else}}
<p>This is not a box item</p>
{{/if}}
{{/each}}
I have tried registering my own helper but haven't managed to make this work.
How can I can get this to work using the Handlebars.Net port (not the HandlebarsJS version)?
EDIT:
The helper I tried using that doesn't work is this:
handlebars.RegisterHelper("eq_test", (a, b) => a.ToString().Equals(b.ToString()));
and it is used in the template like this:
{{#each Box.BoxItems }}
{{#if (eq_test this "item") }}
<p>This is a box item</p>
{{else}}
<p>This is not a box item</p>
{{/if}}
{{/each}}
Answering my own question,
I registered the helper like this:
handlebars.RegisterHelper("isEq", (ctx, args) => {
if (args.Length != 2)
{
throw new ArgumentOutOfRangeException();
}
string str1 = args[0].ToString();
string str2 = args[1].ToString();
return str1 == str2;
});
And then I used it in the template like this:
{{#each Box.BoxItems }}
{{#if (isEq this item) }}
<p>This is a box item</p>
{{else}}
<p>This is not a box item</p>
{{/if}}
{{/each}}
Obviously this is not a perfect example of how to do this. There is no real checking done on the parameters and it is clearly a string comparison.
But hopefully it will save somebody a few hours of figuring out how to register a simple equality check helper for Handlebars.Net.

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

Meteor example for newbie

Following an example from a book, I have in my .js file
lists = new Mongo.Collection("Lists");
if (Meteor.isClient) {
Template.categories.helpers ({
'Category': function(){
return lists.find({}, {sort: {Category: 1}});
}})
and in my html file:
<body>
<div id="categories-container">
{{> categories}}
</div>
</body>
<template name="categories">
<div class="title">my stuff</div>
{{#each lists}}
<div>
{{Category}}
</div>
{{/each}}
</template>
I have input data this data into lists which uses Category for a field using the console:
> lists.insert({Category:"DVDs", items: {Name:"Mission Impossible"
,Owner:"me",LentTo:"Alice"}});
> lists.insert({Category:"Tools", items: {Name:"Linear Compression
Wrench",Owner:"me",LentTo: "STEVE"}});
but it doesn't output the data.
I think the issue lies in the context of your {{#each}}, you seem to be attempting to access the mongo collection itself rather specific fields through a helper function.
Check out this tutorial on spacebars, it’s a pretty good read http://meteorcapture.com/spacebars/ and also the Meteor Docs section http://docs.meteor.com/#/full/livehtmltemplates. I'm fairly new to Meteor myself but hope that helps.
In your example, you use name Category for 2 different things.
It is the name of template helper function that return an array of list items.
and it is the name of a field item inside your list.
Do the {{#each Category}} to run on the array returned by the helper
Inside the each, you should access the {{Category}} or {{item}} list items
You just need to call your helper lists instead of Category and your code will work:
Template.categories.helpers({
lists: function () {
return lists.find({}, {sort: {Category: 1}});
}
});
{{#each lists}} means: Iterate over a collection cursor (the result of a find) or an array which we get by calling the helper lists. The naming here is somewhat confusing because lists also happens to be the name of the collection your are iterating over.
Inside of the each, the context is a lists document. Each document contains a Category field, which you are outputting inside of a div.
Recommended reading:
A Guide to Meteor Templates & Data Contexts
check to run command : meteor list
it is given to you list of packages which is you used
if autopublish package not in the list you want to publish and subscribe your data here's simple example:
//in server
Meteor.publish('lists',function(){
return lists.find({}, {sort: {Category: 1}});
})
//in client
Meteor.subscribe('lists');
For More Deatails : publish subscription
First: The function in your 'categories' helper shouldn't be between quotes (not sure if that may couse problems).
Second: You are using the {{#each}} loop wrong.
You are calling the 'Lists' collection, but you should call the 'Categories' helper.
<template name="categories">
<div class="title">my stuff</div>
{{#each Category}}
<div>
{{Category}}
</div>
{{/each}}
</template>
Its very confusing your helper has the same name as the field you are calling in the #each loop.

How to properly find a conditionally rendered element from onRendered callback?

I’d like to find a conditionally rendered element after the template is rendered.
The template:
<template name="one">
<div class="normal">Normal</div>
{{#if active}}
<div class="conditional">Conditional</div>
{{/if}}
</template>
The code:
Template.one.onRendered(function() {
console.log(this.find(".normal"));
console.log(this.find(".conditional");
}
The above will log the ‘.normal’ but null for the ‘.conditional’ (the condition is true, both elements are present in the final DOM). The reason for that is well documented: onRender runs before {{#if}}s. But I want to run after the whole template is rendered, with all its {{#if}}s.
Is there a way to find that .conditional without making it a separate template?
I'm facing the same issue. This is because what's inside the {{#if}} block is rendered as a dynamically generated template.
So, the best workaround I've found so far is to render a specific template inside the {{#if}} block and add a callback to this specific template:
Here's what you should do:
<template name="one">
<div class="normal">Normal</div>
{{#if active}}
{{> activeTemplate}}
{{/if}}
</template>
<template name="activeTemplate">
<div class="conditional">Conditional</div>
</template>
Template.activeTemplate.onRendered(function() {
console.log(this.find(".conditional");
}
You won't find the DOM element because it doesn't exist.
It doesn't exist because active is false.
If you want active to return true:
Template.one.helpers({
"active": function() { return true; }
})

With clause changes keypath

I have the following template:
{{#each Posts}}
{{#with { Post: this } }}
<h2 on-click="doSomething">{{Title}}</h2>
...
{{/with}}
{{/each}}
When I click on the header and doSomething get called, I get "${{Post:Posts-0}}" in event keypath. But I need to get access to the post keypath: "Posts.0" to modify some of its properties. What is the right way to achieve that?
Using a {{#with { a: b } }} block for aliasing in Ractive has some limitations, as it's not true aliasing, it's simply creating an expression with an object literal. It's a needed enhancement to offer true aliasing with something like {{#each Post in Posts}} or {{#each Posts as Post}}.
As far as what you can do today, you can add the keypath to the with block:
{{#with { Post: this, keypath: #keypath } }}
And then either pass in:
<h2 on-click="doSomething:{{keypath}}">{{Title}}</h2>
Or access in the event via this.event.context.keypath. See http://jsfiddle.net/w0npbnrz/ for both of these in action.
You also could use {{#each Posts:p}} in which case you could get the keypath via 'Posts.' + this.event.index.p.

Resources