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

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.

Related

Handlebars If Statement not behaving as expected

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.

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.

More elegant helper for Meteor 0.9.3 templates

I've created a custom block helper for Meteor 0.9.3 using information from the following URL:
https://github.com/meteor/meteor/wiki/Using-Blaze#new-pattern-for-defining-custom-block-helpers
It can be used within a Spacebars template like so:
{{#ifEqual value1="stringOrNumber_1" value2="stringOrNumber_2"}}
<h1>The values ARE equal!</h1>
{{else}}
<h1>The values are NOT equal!</h1>
{{/ifEqual}}
Here is the code:
<template name="ifEqual">
{{#if isEqual value1 value2}}
{{> UI.contentBlock}}
{{else}}
{{> UI.elseBlock}}
{{/if}}
</template>
Template.ifEqual.isEqual = function (value1, value2) {
return value1 === value2;
};
My question is:
Is there a possible way to make this less clunky?
I would much prefer code that can be used within a template like so:
{{#ifEqual "stringOrNumber_1" "stringOrNumber_2"}}
...
I would register a global equals helper:
Template.registerHelper('equals', function(value1, value2){
return value1 === value2
})
And then use it like this:
{{#if equals "abc" "abc"}}
<p>They're equal :)</p>
{{else}}
<p>They're not equal :(</p>
{{/if}}
Though, you may be interested in using the underscore-helper package instead, so you don't have to define this (and similar) helpers yourself.

Is it possible to assign a parameter value within handlebars templates without using helpers?

I am trying to assign values within a template, the idea is to do something like this:
{{#if author}}
{{className = 'classA'}} <- trying to implement this line.
{{else}}
{{className = 'classB'}}
{{/if}}
<div class={{className}}></div>
Is it possible to do so without registerHelper?
I needed this solution solved in the same way the original poster expected. Therefore I created a helper function to do this for me.
function setVariable(varName, varValue, options){
options.data.root[varName] = varValue;
};
Contained within the options in the helper, is the data block, with the root of the helper variables.
I placed my variable within the root object, either assigning or overwriting with the value provided.
The helper in html looks a lot like this.
{{setVariable "thisVar" "Contents Here"}}
<div>
{{thisVar}}
</div>
You can do this with a partial.
partial:
<div class="{{classname}}">{{var1.author}}</div>
template:
{{#if author}}
{{> mypartial var1=. classname="classA"}}
{{else}}
{{> mypartial var1=. classname="classB"}}
{{/if}}
In this example I've passed the current scope as var1. I can't recall if the scope stays the same and the new variable is just added to it or if you have to pass the scope manually.
Small update from a helpful above answer. In Handlebars 4.x, it seems we have to get the variables from #root.
handlebars.registerHelper('assign', function (varName, varValue, options) {
if (!options.data.root) {
options.data.root = {};
}
options.data.root[varName] = varValue;
});
And then,
{{assign 'imageTag' 'drop-155'}}
{{#root.imageTag}}
{{#if author}}
<div class='{{ classA }}'></div>
{{else}}
<div class='{{ classB }}'></div>
{{/if}}
or try
<div class='{{#if author}} {{ classA }} {{else}} {{ classB }} {{/if}}'></div>
or maybe this
create a script
<script>
$(function(){
var class;
{{#if author}}
class = {{ classA }}
{{else}}
class = {{ classB }}
{{/if}}
var $specials = $('.special');
$specials.removeClass('special');
$specials.addClass(class);
})
</script>

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