Meteor templates, check if value equals string - javascript

Here's the template structure
{{#each loadedEvents}}
{{#if future}}
{{#if timezone="Europe/Warsaw"}}
{{> event}}
{{/if}}
{{/each}}
Is that possible to view only items with given value?
And the second question, how to combine this two statements:
{{#if future}} {{#if timezone="Europe/Warsaw"}}

You can create a dedicated helper to check if a timezone is equal to a certain value :
Template.loadedEvents.helpers({
timezoneIs: function(timezone){
return this.timezone == timezone;
}
});
If you want to combine two Spacebars {{#if}} block helpers, once again create a dedicated helper that performs the test in JS :
JS
Template.loadedEvents.helpers({
isFutureAndTimezoneIs: function(timezone){
return this.future && this.timezone == timezone;
}
});
HTML
{{#each loadedEvents}}
{{#if isFutureAndTimezoneIs "Europe/Warsaw"}}
{{> event}}
{{/if}}
{{/each}}

Use Template.registerHelper to create a global helper. For instance, to create a helper that compares two arbitrary variables:
Template.registerHelper('compare', function(v1, v2) {
if (typeof v1 === 'object' && typeof v2 === 'object') {
return _.isEqual(v1, v2); // do a object comparison
} else {
return v1 === v2;
}
});
Then use it using:
{{#if compare timezone "Europe/Warsaw"}}
// Do something
{{/if}}

Related

I am gettting the error: Compile Error: ifCond is not a helper when I am trying to write a helper function in Ember

I have a helper function (ifCond) in ember.js as below
export default () => {
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
alert("hi");
if (params[3]) { //handle case insensitive conditions if 4 param is passed.
params[0] = params[0].toLowerCase();
params[2] = params[2].toLowerCase();
}
let v1 = params[0];
let operator = params[1];
let v2 = params[2];
switch (operator) {
case '==':
return (v1 == v2);
case '!=':
return (v1 != v2);
case '===':
return (v1 === v2);
case '<':
return (v1 < v2);
case '<=':
return (v1 <= v2);
case '>':
return (v1 > v2);
case '>=':
return (v1 >= v2);
case '&&':
return !!(v1 && v2);
case '||':
return !!(v1 || v2);
default:
return false;
}
});
}
when I am trying to access this function in my hbs file as below
{{#if (ifCond novv.ViolationId '==' noviv.ViolationId true)}}
{{log 'someVariable'}}
<br />
{{/if}}
I am getting error as below:
Any help please - thanks in advance
From your example it looks like you may be working with a very old version of Ember. The first place to start is with the Ember Guide on writing helpers. There is a version selector in the top right corner of each guide that will take you back to previous documentation. A guess form your example is that you're working with something around 1.11.
I would highly recommend that you update to a newer version of ember and use ember-cli as that will make the import and discovery process for helpers much smoother along with gaining access to newer features and security updates.
Did it my friend, its done.
Here it is, the previous developer put all the helper functions in one file, the confusion he created was, he put other files with those helper functions in different directories - it seems I need to clean that up.
And your suggestion to check the define helped me to look into the webpack.config.js (async helpers, async __parts), there he is combining all the files scripts and generating as one js file which is written on to app.js. The convention he (or Ember I am not sure) followed was camel casing. For example for one helper-function he written as below withing the same ifCond.js file, is converted as "replace", we can use it as replace in handlebar
IMS.ReplaceHelper = Ember.Helper.extend({
compute(args) {
return args[0].replace(new RegExp(args[1], 'ig'), args[2]);
}
})
If suppose, if there are two Words - then it follows the camel casing (you know it). Totally completed and I have written some helper functions of my own as needed - thanks a lot for everybody who jumped-in to help me - thanks a lot and I don't have words how to describe how happy I am - thank you. My own helper functions as below:
IMS.IsLastHelper = Ember.Helper.extend({
compute(args) {
var list = args[0];
var item = args[1];
if (Array.isArray(list)) {
var id = list.indexOf(item);
return id == list.length - 1;
}
return false;
}
})
IMS.IsFirstHelper = Ember.Helper.extend({
compute(args) {
var list = args[0];
var item = args[1];
if (Array.isArray(list)) {
var id = list.indexOf(item);
return id == 0;
}
return false;
}
})
And I called them with syntax as below:
{{#each model.novs as |nov index|}}
{{#if (isFirst model.novs nov)}}
({{nov.NOVNumber}}:
{{else}}
{{nov.NOVNumber}}:
{{/if}}
{{#each nov.Violations as |novv index|}}
{{#unless (isLast nov.Violations novv)}}
{{novv.ViolationNumber}},
{{else}}
{{#if (isLast model.novs nov)}}
{{novv.ViolationNumber}}
{{else}}
{{novv.ViolationNumber}};
{{/if}}
{{/unless}}
{{/each}}
{{#if (isLast model.novs nov)}}
)
{{/if}}
{{/each}}
Thanks again to everybody in this group. :pray:

Check status in handlebars

Hi I want to check the string response in HB. I've tried this:
{{#if status=='false'}}
{{console.log("hi");}}
{{else}}
{{console.log("no");}}
{{#endif}}
How can I check the response simple? I want to show a message if it's true and another message if it's false.
Handlebars is designed to be very simple and doesn't have this functionality out of the box. You should pass the status as a boolean rather than a string, then just use an if statement:
{{#if status}}
{{console.log("hi");}}
{{else}}
{{console.log("no");}}
{{#endif}}
You could also write a helper function:
Handlebars.registerHelper('ifEq', function(a, b, options) {
if (a == b) return options.fn(this)
else return options.inverse(this)
});
Then your handlebars becomes:
{{#ifEq status 'true'}}
Hello
{{else}}
No
{{/ifEq}}

Handlebars Helper to Compare Values (if v1 === v2), and Render Upper-Level Scope?

For the actual call, I need something like this:
<script id="messagesTemplate" type="text/x-handlebars-template">
{{#each messages.messages}}
{{#each to}}
{{#ifCond username messages.sessionUserName}}
<h1>{{username}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{username}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
{{/each}}
{{/each}}
Where, in the db, 'to' is an array of docs that each have a 'username'..that thus need === the messages.sessionUserName to then template/render HTML for certain values (e.g. {{#if read.marked}} )
"to" : [
{
"user" : ObjectId("53aada6f8b10eb0000ec8a90"),
"username" : "username1",
"updated" : ISODate("2014-07-01T19:39:45Z"),
"_id" : ObjectId("53b30e81b0eff5cb1e2ecb21"),
"read" : {
"marked" : true
}
}
]
Worth noting, both usernameTest & sessionUserName are values appended to the end of the res.json() via express, so they are accessible by messages.usernameTest & messages.sessionUserName, but they are not present in each document..these values are only available in the global parent doc.
res.json({
messages : messages,
sessionUserName: req.session.username,
usernameTest: usernameTest
});
This factor may be responsible for why each of these only render is equal to, but doesn't really make sense for the third (The ../ path segment references the parent template scope):
{{#each messages.messages}}
<h1>{{usernameTest}} is equal to {{sessionUserName}}</h1>
<h1>{{../usernameTest}} is equal to {{../sessionUserName}}</h1>
<h1>{{../messages.usernameTest}} is equal to {{../messages.sessionUserName}}</h1>
Drawing from https://stackoverflow.com/a/9405113/3095287 for the custom comparison helper, the template that follows {{#ifCond v1 v2}} doesn't seem to render upper-level scoped elements..
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
The ifCond comparison does work outside of an {{#each}} block:
<script id="messagesTemplate" type="text/x-handlebars-template">
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{messages.usernameTest}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{messages.usernameTest}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
{{#each messages.messages}}
..
..as that renders:
username1 is equal to username1
However, it does not work inside of an {{#each}} block:
{{#each messages.messages}}
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{messages.usernameTest}} is equal to {{messages.sessionUserName}}</h1>
{{else}}
<h1>{{messages.usernameTest}} is not equal to {{messages.sessionUserName}}</h1>
{{/ifCond}}
...
..as it only renders:
is equal to
Even with {{../element}}
{{#each messages.messages}}
{{#ifCond messages.usernameTest messages.sessionUserName}}
<h1>{{../messages.usernameTest}} is equal to {{../messages.sessionUserName}}</h1>
{{else}}
<h1>{{../messages.usernameTest}} is not equal to {{../messages.sessionUserName}}</h1>
{{/ifCond}}
...
..the rendering is:
is equal to
Ok so the main thing is you want to be able to gain access to your top level scope deeper down. Ive done this using a customer helper that adds a little extra to the normal each block.
so here is the normal handle bars each
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
all i do is set 'this' to a property called root and pass it back with the result. To overcome nested loops i check for the existence of my 'root' property and if it exists I pass it along other wise root = this.
Handlebars.registerHelper("myEach", function(context, options) {
var ret = "";
for (var i = 0, j = context.length; i < j; i++) {
if (this.root) {
root = this.root;
} else {
root = this;
}
ret = ret + options.fn(_.extend({}, context[i], {
root: root
}));
}
return ret;
});
Now no matter how deep i am in my loops if i want to use something from the root i just use root.property.
A working codepen can be found here with a simplified version of your example.
EDIT: Ok so 5 minutes later after posting this i read about paths in another templating language and then realise handlebars also has paths. so you don't need to do the above you can just use the relative nested path in your template like below. I'm going to continue using the helper though as I think it is tidier to go root.property rather than adding n many "../" for how nested your are.
here is a working example using the paths
<script type="text/x-handlebars-template" id="messages-template">
Logged in user {{userSession}}
{{#each messages}}
<ul>
<li> Title: {{title}}</li>
<li> Conetent: {{content}}</li>
<li> TO:
<ul>
{{#each to}}
<li>{{user}} {{#ifvalue user ../../userSession}}
thats me
{{else}}
thats not me
{{/ifvalue}}</li>
{{/each}}
</ul>
</li>
</ul>
{{/each}}
</script>

Handlerbars.js using an helper function in a #if statement

With handlebars.js I want to display two blocks of html depending of a resulting json.
Let's say I want to thanks my user for ordering items at my shop.
I write my handlerbars.js template like this :
<p>{{name}}</p>
{{#if costIsZero}}
Can't find any order
{{else}}
You bought {{cost}} items in our shop, thanks.
{{/if}}
I'm coding a simple helper for costIsZero like this :
Handlebars.registerHelper('costIsZero', function(){
return this.cost == 0
});
When I mix it with the following json data :
var data = {
"name":"foo",
"cost": 9
};
Whatever the value of "cost" is the {{#if costIsZero}} seems always to be true.
If I comment out the helper itself, thus having nothing for costIsZero it returns always false.
All the code above is available as a JSFiddle there http://jsfiddle.net/gsSyt/
What I'm doing wrong ?
Maybe I'm hijacking the way handlebars.js work, but in that case, How should I implement my feature with handlebars.js ?
Helpers are not invoked when evaluating an expression such as costIsZero.
You could create a custom helper that works as an alternative to if:
Handlebars.registerHelper('ifCostIsZero', function(block) {
if (this.cost == 0) {
return block(this);
} else {
return block.inverse(this);
}
});
Which you would use like this:
{{#ifCostIsZero}}
Can't find any order
{{else}}
You bought {{cost}} items in our shop, thanks.
{{/ifCostIsZero}}
Alternatively, you can use the stock if (or unless) since your test is against zero :
{{#if cost}}
You bought {{cost}} items in our shop, thanks.
{{else}}
Can't find any order
{{/if}}
You can play with both options at http://jsfiddle.net/gsSyt/41/
Try this:
Handlebars.registerHelper('if', function(conditional, block) {
if(this.cost == 0) {
return block(this);
} else {
return block.inverse(this);
}
})
http://jsfiddle.net/mZbtk/2/

Logical operator in a handlebars.js {{#if}} conditional

Is there a way in handlebars JS to incorporate logical operators into the standard handlebars.js conditional operator? Something like this:
{{#if section1 || section2}}
.. content
{{/if}}
I know I could write my own helper, but first I'd like to make sure I'm not reinventing the wheel.
This is possible by 'cheating' with a block helper. This probably goes against the Ideology of the people who developed Handlebars.
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
You can then call the helper in the template like this
{{#ifCond v1 v2}}
{{v1}} is equal to {{v2}}
{{else}}
{{v1}} is not equal to {{v2}}
{{/ifCond}}
Taking the solution one step further. This adds the compare operator.
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
switch (operator) {
case '==':
return (v1 == v2) ? options.fn(this) : options.inverse(this);
case '===':
return (v1 === v2) ? options.fn(this) : options.inverse(this);
case '!=':
return (v1 != v2) ? options.fn(this) : options.inverse(this);
case '!==':
return (v1 !== v2) ? options.fn(this) : options.inverse(this);
case '<':
return (v1 < v2) ? options.fn(this) : options.inverse(this);
case '<=':
return (v1 <= v2) ? options.fn(this) : options.inverse(this);
case '>':
return (v1 > v2) ? options.fn(this) : options.inverse(this);
case '>=':
return (v1 >= v2) ? options.fn(this) : options.inverse(this);
case '&&':
return (v1 && v2) ? options.fn(this) : options.inverse(this);
case '||':
return (v1 || v2) ? options.fn(this) : options.inverse(this);
default:
return options.inverse(this);
}
});
Use it in a template like this:
{{#ifCond var1 '==' var2}}
Coffee Script version
Handlebars.registerHelper 'ifCond', (v1, operator, v2, options) ->
switch operator
when '==', '===', 'is'
return if v1 is v2 then options.fn this else options.inverse this
when '!=', '!=='
return if v1 != v2 then options.fn this else options.inverse this
when '<'
return if v1 < v2 then options.fn this else options.inverse this
when '<='
return if v1 <= v2 then options.fn this else options.inverse this
when '>'
return if v1 > v2 then options.fn this else options.inverse this
when '>='
return if v1 >= v2 then options.fn this else options.inverse this
when '&&', 'and'
return if v1 and v2 then options.fn this else options.inverse this
when '||', 'or'
return if v1 or v2 then options.fn this else options.inverse this
else
return options.inverse this
Handlebars supports nested operations. This provides a lot of flexibility (and cleaner code) if we write our logic a little differently.
{{#if (or section1 section2)}}
.. content
{{/if}}
In fact, we can add all sorts of logic:
{{#if (or
(eq section1 "foo")
(ne section2 "bar"))}}
.. content
{{/if}}
Just register these helpers:
Handlebars.registerHelper({
eq: (v1, v2) => v1 === v2,
ne: (v1, v2) => v1 !== v2,
lt: (v1, v2) => v1 < v2,
gt: (v1, v2) => v1 > v2,
lte: (v1, v2) => v1 <= v2,
gte: (v1, v2) => v1 >= v2,
and() {
return Array.prototype.every.call(arguments, Boolean);
},
or() {
return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
}
});
taking this one up a notch, for those of you who live on the edge.
gist: https://gist.github.com/akhoury/9118682
Demo: Code snippet below
Handlebars Helper: {{#xif EXPRESSION}} {{else}} {{/xif}}
a helper to execute an IF statement with any expression
EXPRESSION is a properly escaped String
Yes you NEED to properly escape the string literals or just alternate single and double quotes
you can access any global function or property i.e. encodeURIComponent(property)
this example assumes you passed this context to your handlebars template( {name: 'Sam', age: '20' } ), notice age is a string, just for so I can demo parseInt() later in this post
Usage:
<p>
{{#xif " name == 'Sam' && age === '12' " }}
BOOM
{{else}}
BAMM
{{/xif}}
</p>
Output
<p>
BOOM
</p>
JavaScript: (it depends on another helper- keep reading)
Handlebars.registerHelper("xif", function (expression, options) {
return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});
Handlebars Helper: {{x EXPRESSION}}
A helper to execute javascript expressions
EXPRESSION is a properly escaped String
Yes you NEED to properly escape the string literals or just alternate single and double quotes
you can access any global function or property i.e. parseInt(property)
this example assumes you passed this context to your handlebars template( {name: 'Sam', age: '20' } ), age is a string for demo purpose, it can be anything..
Usage:
<p>Url: {{x "'hi' + name + ', ' + window.location.href + ' <---- this is your href,' + ' your Age is:' + parseInt(this.age, 10)"}}</p>
Output:
<p>Url: hi Sam, http://example.com <---- this is your href, your Age is: 20</p>
JavaScript:
This looks a little large because I expanded syntax and commented over almost each line for clarity purposes
Handlebars.registerHelper("x", function(expression, options) {
var result;
// you can change the context, or merge it with options.data, options.hash
var context = this;
// yup, i use 'with' here to expose the context's properties as block variables
// you don't need to do {{x 'this.age + 2'}}
// but you can also do {{x 'age + 2'}}
// HOWEVER including an UNINITIALIZED var in a expression will return undefined as the result.
with(context) {
result = (function() {
try {
return eval(expression);
} catch (e) {
console.warn('•Expression: {{x \'' + expression + '\'}}\n•JS-Error: ', e, '\n•Context: ', context);
}
}).call(context); // to make eval's lexical this=context
}
return result;
});
Handlebars.registerHelper("xif", function(expression, options) {
return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});
var data = [{
firstName: 'Joan',
age: '21',
email: 'joan#aaa.bbb'
}, {
firstName: 'Sam',
age: '18',
email: 'sam#aaa.bbb'
}, {
firstName: 'Perter',
lastName: 'Smith',
age: '25',
email: 'joseph#aaa.bbb'
}];
var source = $("#template").html();
var template = Handlebars.compile(source);
$("#main").html(template(data));
h1 {
font-size: large;
}
.content {
padding: 10px;
}
.person {
padding: 5px;
margin: 5px;
border: 1px solid grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js"></script>
<script id="template" type="text/x-handlebars-template">
<div class="content">
{{#each this}}
<div class="person">
<h1>{{x "'Hi ' + firstName"}}, {{x 'lastName'}}</h1>
<div>{{x '"you were born in " + ((new Date()).getFullYear() - parseInt(this.age, 10)) '}}</div>
{{#xif 'parseInt(age) >= 21'}} login here:
<a href="http://foo.bar?email={{x 'encodeURIComponent(email)'}}">
http://foo.bar?email={{x 'encodeURIComponent(email)'}}
</a>
{{else}} Please go back when you grow up. {{/xif}}
</div>
{{/each}}
</div>
</script>
<div id="main"></div>
Moar
if you want access upper level scope, this one is slightly different, the expression is the JOIN of all arguments,
usage: say context data looks like this:
// data
{name: 'Sam', age: '20', address: { city: 'yomomaz' } }
// in template
// notice how the expression wrap all the string with quotes, and even the variables
// as they will become strings by the time they hit the helper
// play with it, you will immediately see the errored expressions and figure it out
{{#with address}}
{{z '"hi " + "' ../this.name '" + " you live with " + "' city '"' }}
{{/with}}
Javascript:
Handlebars.registerHelper("z", function () {
var options = arguments[arguments.length - 1]
delete arguments[arguments.length - 1];
return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]);
});
Handlebars.registerHelper("zif", function () {
var options = arguments[arguments.length - 1]
delete arguments[arguments.length - 1];
return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]) ? options.fn(this) : options.inverse(this);
});
There is a simple way of doing this without writing a helper function... It can be done within the template completely.
{{#if cond1}}
{{#if con2}}
<div> and condition completed</div>
{{/if}}
{{else}}
<div> both conditions weren't true</div>
{{/if}}
Edit: Conversely you can do or's by doing this:
{{#if cond1}}
<div> or condition completed</div>
{{else}}
{{#if cond2}}
<div> or condition completed</div>
{{else}}
<div> neither of the conditions were true</div>
{{/if}}
{{/if}}
Edit/Note: From the handlebar's website: handlebarsjs.com here are the falsy values:
You can use the if helper to conditionally render a block. If its
argument returns false, undefined, null, "" or [] (a "falsy" value),
Then any 'cond' (like cond1 or cond2) will not be counted as true.
One problem with all of the answers posted here is that they don't work with bound properties, i.e. the if condition is not re-evaluated when the properties involved change. Here's a slightly more advanced version of the helper supporting bindings. It uses the bind function from the Ember source, which is also used to implement the normal Ember #if helper.
This one is limited to a single bound property on the left-hand side, comparing to a constant on the right-hand side, which I think is good enough for most practical purposes. If you need something more advanced than a simple comparison, then perhaps it would be good to start declaring some computed properties and using the normal #if helper instead.
Ember.Handlebars.registerHelper('ifeq', function(a, b, options) {
return Ember.Handlebars.bind.call(options.contexts[0], a, options, true, function(result) {
return result === b;
});
});
You can use it like this:
{{#ifeq obj.some.property "something"}}
They are equal!
{{/ifeq}}
Improved solution that basically work with any binary operator (at least numbers, strings doesn't work well with eval, TAKE CARE OF POSSIBLE SCRIPT INJECTION IF USING A NON DEFINED OPERATOR WITH USER INPUTS):
Handlebars.registerHelper("ifCond",function(v1,operator,v2,options) {
switch (operator)
{
case "==":
return (v1==v2)?options.fn(this):options.inverse(this);
case "!=":
return (v1!=v2)?options.fn(this):options.inverse(this);
case "===":
return (v1===v2)?options.fn(this):options.inverse(this);
case "!==":
return (v1!==v2)?options.fn(this):options.inverse(this);
case "&&":
return (v1&&v2)?options.fn(this):options.inverse(this);
case "||":
return (v1||v2)?options.fn(this):options.inverse(this);
case "<":
return (v1<v2)?options.fn(this):options.inverse(this);
case "<=":
return (v1<=v2)?options.fn(this):options.inverse(this);
case ">":
return (v1>v2)?options.fn(this):options.inverse(this);
case ">=":
return (v1>=v2)?options.fn(this):options.inverse(this);
default:
return eval(""+v1+operator+v2)?options.fn(this):options.inverse(this);
}
});
Here's a solution if you want to check multiple conditions:
/* Handler to check multiple conditions
*/
Handlebars.registerHelper('checkIf', function (v1,o1,v2,mainOperator,v3,o2,v4,options) {
var operators = {
'==': function(a, b){ return a==b},
'===': function(a, b){ return a===b},
'!=': function(a, b){ return a!=b},
'!==': function(a, b){ return a!==b},
'<': function(a, b){ return a<b},
'<=': function(a, b){ return a<=b},
'>': function(a, b){ return a>b},
'>=': function(a, b){ return a>=b},
'&&': function(a, b){ return a&&b},
'||': function(a, b){ return a||b},
}
var a1 = operators[o1](v1,v2);
var a2 = operators[o2](v3,v4);
var isTrue = operators[mainOperator](a1, a2);
return isTrue ? options.fn(this) : options.inverse(this);
});
Usage:
/* if(list.length>0 && public){}*/
{{#checkIf list.length '>' 0 '&&' public '==' true}} <p>condition satisfied</p>{{/checkIf}}
Here's a link to the block helper I use: comparison block helper. It supports all the standard operators and lets you write code as shown below. It's really quite handy.
{{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
{{/compare}}
Install Ember Truth Helpers addon by running the below command
ember install ember-truth-helpers
you can start use most of the logical operators(eq,not-eq,not,and,or,gt,gte,lt,lte,xor).
{{#if (or section1 section2)}}
...content
{{/if}}
You can even include subexpression to go further,
{{#if (or (eq section1 "section1") (eq section2 "section2") ) }}
...content
{{/if}}
Yet another crooked solution for a ternary helper:
'?:' ( condition, first, second ) {
return condition ? first : second;
}
<span>{{?: fooExists 'found it' 'nope, sorry'}}</span>
Or a simple coalesce helper:
'??' ( first, second ) {
return first ? first : second;
}
<span>{{?? foo bar}}</span>
Since these characters don't have a special meaning in handlebars markup, you're free to use them for helper names.
Similar to Jim's answer but a using a bit of creativity we could also do something like this:
Handlebars.registerHelper( "compare", function( v1, op, v2, options ) {
var c = {
"eq": function( v1, v2 ) {
return v1 == v2;
},
"neq": function( v1, v2 ) {
return v1 != v2;
},
...
}
if( Object.prototype.hasOwnProperty.call( c, op ) ) {
return c[ op ].call( this, v1, v2 ) ? options.fn( this ) : options.inverse( this );
}
return options.inverse( this );
} );
Then to use it we get something like:
{{#compare numberone "eq" numbertwo}}
do something
{{else}}
do something else
{{/compare}}
I would suggest moving the object out of the function for better performance but otherwise you can add any compare function you want, including "and" and "or".
One other alternative is to use function name in #if. The #if will detect if the parameter is function and if it is then it will call it and use its return for truthyness check. Below myFunction gets current context as this.
{{#if myFunction}}
I'm Happy!
{{/if}}
Unfortunately none of these solutions solve the problem of "OR" operator "cond1 || cond2".
Check if first value is true
Use "^" (or) and check if otherwise cond2 is true
{{#if cond1}}
DO THE ACTION
{{^}}
{{#if cond2}}
DO THE ACTION
{{/if}}
{{/if}}
It breaks DRY rule. So why not use partial to make it less messy
{{#if cond1}}
{{> subTemplate}}
{{^}}
{{#if cond2}}
{{> subTemplate}}
{{/if}}
{{/if}}
I can understand why you would want to create a helper for situations where you have a large number of varied comparisons to perform within your template, but for a relatively small number of comparisons (or even one, which was what brought me to this page in the first place), it would probably just be easier to define a new handlebars variable in your view-rendering function call, like:
Pass to handlebars on render:
var context= {
'section1' : section1,
'section2' : section2,
'section1or2' : (section1)||(section2)
};
and then within your handlebars template:
{{#if section1or2}}
.. content
{{/if}}
I mention this for simplicity's sake, and also because it's an answer that may be quick and helpful while still complying with the logicless nature of Handlebars.
I have found a npm package made with CoffeeScript that has a lot of incredible useful helpers for Handlebars. Take a look of the documentation in the following URL:
https://npmjs.org/package/handlebars-helpers
You can do a wget http://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.2.6.tgz to download them and see the contents of the package.
You will be abled to do things like {{#is number 5}} or {{formatDate date "%m/%d/%Y"}}
Correct Solution for AND/OR
Handlebars.registerHelper('and', function () {
// Get function args and remove last one (function name)
return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);
});
Handlebars.registerHelper('or', function () {
// Get function args and remove last one (function name)
return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);
});
Then call as follows
{{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }}
BTW: Note that the solution given here is incorrect, he's not subtracting the last argument which is the function name.
https://stackoverflow.com/a/31632215/1005607
His original AND/OR was based on the full list of arguments
and: function () {
return Array.prototype.slice.call(arguments).every(Boolean);
},
or: function () {
return Array.prototype.slice.call(arguments).some(Boolean);
}
Can someone change that answer? I just wasted an hour trying to fix something in an answer recommended by 86 people. The fix is to filter out the last argument which is the function name. Array.prototype.slice.call(arguments, 0, arguments.length - 1)
if you just want to check if one or the other element are present you can use this custom helper
Handlebars.registerHelper('if_or', function(elem1, elem2, options) {
if (Handlebars.Utils.isEmpty(elem1) && Handlebars.Utils.isEmpty(elem2)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
like this
{{#if_or elem1 elem2}}
{{elem1}} or {{elem2}} are present
{{else}}
not present
{{/if_or}}
if you also need to be able to have an "or" to compare
function return values
I would rather add another property that returns the desired result.
The templates should be logicless after all!
For those having problems comparing object properties, inside the helper add this solution
Ember.js helper not properly recognizing a parameter
Just came to this post from a google search on how to check if a string equals another string.
I use HandlebarsJS in NodeJS server-side, but I also use the same template files on the front-end using the browser version of HandlebarsJS to parse it. This meant that if I wanted a custom helper, I'd have to define it in 2 separate places, or assign a function to the object in question - too much effort!!
What people forget is that certain objects have inherit functions that can be used in the moustache template. In the case of a string:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
An Array containing the entire match result and any parentheses-captured matched results; null if there were no matches.
We can use this method to return either an array of matches, or null if no matches were found. This is perfect, because looking at the HandlebarsJS documentation http://handlebarsjs.com/builtin_helpers.html
You can use the if helper to conditionally render a block. If its argument returns false, undefined, null, "", 0, or [], Handlebars will not render the block.
So...
{{#if your_string.match "what_youre_looking_for"}}
String found :)
{{else}}
No match found :(
{{/if}}
UPDATE:
After testing on all browsers, this doesn't work on Firefox. HandlebarsJS passes other arguments to a function call, meaning that when String.prototype.match is called, the second argument (i.e. the Regexp flags for the match function call as per above documentation) appears to be being passed. Firefox sees this as a deprecated use of String.prototype.match, and so breaks.
A workaround is to declare a new functional prototype for the String JS object, and use that instead:
if(typeof String.includes !== 'function') {
String.prototype.includes = function(str) {
if(!(str instanceof RegExp))
str = new RegExp((str+'').escapeRegExp(),'g');
return str.test(this);
}
}
Ensure this JS code is included before you run your Handlebars.compile() function, then in your template...
{{#your_string}}
{{#if (includes "what_youre_looking_for")}}
String found :)
{{else}}
No match found :(
{{/if}}
{{/your_string}}
You can do it simply by using the logical operator like this shown below:
{{#if (or(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}
{{#if (and(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}
Before closing if you can write your business logic
Here we have vanilla handlebars for multiple logical && and || (and or):
Handlebars.registerHelper("and",function() {
var args = Array.prototype.slice.call(arguments);
var options = args[args.length-1];
for(var i=0; i<args.length-1; i++){
if( !args[i] ){
return options.inverse(this);
}
}
return options.fn(this);
});
Handlebars.registerHelper("or",function() {
var args = Array.prototype.slice.call(arguments);
var options = args[args.length-1];
for(var i=0; i<args.length-1; i++){
if( args[i] ){
return options.fn(this);
}
}
return options.inverse(this);
}
// Results
// {{#and foo bar sally bob}} yup {{else}} nope {{/and}} // yup
// {{#or foo bar "" sally bob}} yup {{else}} nope {{/or}} // yup
// {{#and foo bar "" sally bob}} yup {{else}} nope {{/and}} // nope
// {{#or "" "" "" "" ""}} yup {{else}} nope {{/or}} // nope
Not so sure if it's "safe" to use "and" and "or"... maybe change to something like "op_and" and "op_or"?
Following these 2 guides a-way-to-let-users-define-custom-made-bound-if-statements and custom bound helpers I was able to adjust my shared views in this post on stackoverflow to use this instead of the standard #if statement. This should be more secure than just tossing an #if in there.
The custom bound helpers in that gist are outstanding.
<li>
<a href="{{unbound view.varProductSocialBlog}}">
{{#if-equal view.showDiv "true"}}<div>{{/if-equal}}<i class="fa fa-rss-square"></i>{{#if-equal view.showDiv "true"}}</div>{{/if-equal}}
{{#if-equal view.showTitle "true"}}Blog{{/if-equal}}
</a>
</li>
I am using the ember cli project to build my ember application.
Current setup at the time of this post:
DEBUG: -------------------------------
DEBUG: Ember : 1.5.1
DEBUG: Ember Data : 1.0.0-beta.7+canary.b45e23ba
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 2.1.1
DEBUG: -------------------------------
In Ember.js you can use inline if helper in if block helper. It can replace || logical operator, for example:
{{#if (if firstCondition firstCondition secondCondition)}}
(firstCondition || (or) secondCondition) === true
{{/if}}
You can use the following code:
{{#if selection1}}
doSomething1
{{else}}
{{#if selection2}}
doSomething2
{{/if}}
{{/if}}
you cannot write your expressions inside handlebars template but all of your logic (expressions) in express.js
app.js
res.render("view.hbs", {expression: section1 || section2})
view.hbs
{{#if expression}}
<h1> My Expression Returned True </h1>
{{ else }}
<h2>My Expression Returned False</h2>
{{/if}} <!-- End IF -->
Here's an approach I'm using for ember 1.10 and ember-cli 2.0.
// app/helpers/js-x.js
export default Ember.HTMLBars.makeBoundHelper(function (params) {
var paramNames = params.slice(1).map(function(val, idx) { return "p" + idx; });
var func = Function.apply(this, paramNames.concat("return " + params[0] + ";"))
return func.apply(params[1] === undefined ? this : params[1], params.slice(1));
});
Then you can use it in your templates like this:
// used as sub-expression
{{#each item in model}}
{{#if (js-x "this.section1 || this.section2" item)}}
{{/if}}
{{/each}}
// used normally
{{js-x "p0 || p1" model.name model.offer.name}}
Where the arguments to the expression are passed in as p0,p1,p2 etc and p0 can also be referenced as this.

Categories