handlebars.js {{#if}}条件中的逻辑运算符

在handlebars JS中是否有一种方法可以将逻辑操作符合并到标准handlebars. JS条件操作符中?就像这样:

{{#if section1 || section2}}
.. content
{{/if}}

我知道我可以编写自己的助手,但首先我想确保我没有重复工作。

638181 次浏览

这可以通过使用block helper来“欺骗”。这可能违背了开发车把的人的意识形态。

Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});

然后可以像这样调用模板中的helper

\{\{#ifCond v1 v2}}
\{\{v1}} is equal to \{\{v2}}
\{\{else}}
\{\{v1}} is not equal to \{\{v2}}
\{\{/ifCond}}

更进一步的解决方案。这将添加比较操作符。

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);
}
});

在模板中使用它,就像这样:

\{\{#ifCond var1 '==' var2}}

脚本版本

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

有一种简单的方法可以做到这一点,而不需要编写helper函数…它完全可以在模板中完成。

\{\{#if cond1}}
\{\{#if con2}}
<div> and condition completed</div>
\{\{/if}}
\{\{else}}
<div> both conditions weren't true</div>
\{\{/if}}

编辑:相反,你可以这样做或's:

\{\{#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}}

编辑/注:来自车把网站:handlebarsjs.com的错误值如下:

你可以使用if helper有条件地呈现一个块。如果它的 参数返回false, undefined, null, "或[](a " false "值), 那么任何'cond'(如cond1或cond2)将不会被视为真

我发现了一个用CoffeeScript制作的npm包,它有很多令人难以置信的有用的把手助手。在下面的URL中查看文档:

https://npmjs.org/package/handlebars-helpers

您可以执行wget http://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.2.6.tgz来下载它们并查看包的内容。

您将能够做\{\{#is number 5}}\{\{formatDate date "%m/%d/%Y"}}之类的事情

与Jim的答案类似,但我们也可以使用一点创造力,这样做:

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 );
} );

然后使用它,我们得到如下内容:

\{\{#compare numberone "eq" numbertwo}}
do something
\{\{else}}
do something else
\{\{/compare}}

我建议将对象移出函数以获得更好的性能,否则你可以添加任何你想要的比较函数,包括"和"和“;or"。

如果您只是想检查一个元素或另一个元素是否存在,您可以使用这个自定义帮助器

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);
}
});

像这样

\{\{#if_or elem1 elem2}}
\{\{elem1}} or \{\{elem2}} are present
\{\{else}}
not present
\{\{/if_or}}

如果你还需要一个“或”来比较 函数返回值 我宁愿添加另一个返回所需结果的属性

模板应该是无逻辑的!

改进的解决方案,基本上适用于任何二进制操作符(至少数字,字符串不适用于eval,如果使用用户输入的非定义操作符,请注意可能的脚本注入):

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);
}
});

这里有一个我使用的块助手的链接:比较块帮助器。它支持所有标准操作符,并允许您编写如下所示的代码。真的很方便。

\{\{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
\{\{/compare}}

另一种替代方法是在#if中使用函数名。#if将检测参数是否为函数,如果是,则调用它并使用其返回值进行真实性检查。下面myFunction获取当前上下文为this

\{\{#if myFunction}}
I'm Happy!
\{\{/if}}

不幸的是,这些解决方案都不能解决“或”操作符“cond1 || cond2”的问题。

  1. 检查第一个值是否为真
  2. 使用"^"(或)并检查cond2是否为真

    < p >\{\{#如果cond1}} 行动起来 \{\{^}} \{\{#如果cond2}} 行动起来 \{\{/如果}} \{\{/如果}}< / p > < /李>

这违反了DRY规则。为什么不用偏微分函数来简化呢

\{\{#if cond1}}
\{\{> subTemplate}}
\{\{^}}
\{\{#if cond2}}
\{\{> subTemplate}}
\{\{/if}}
\{\{/if}}

这里发布的所有答案的一个问题是,它们不能与绑定属性一起工作,即当涉及的属性发生变化时,if条件不会重新计算。下面是稍微高级一点的支持绑定的帮助器版本。它使用烬源中的绑定函数,该函数也用于实现正常的烬#if helper。

与右边的常数相比,这一项在左边被限制为一个单一的边界性质,我认为这对于大多数实际目的来说已经足够好了。如果您需要比简单比较更高级的东西,那么最好开始声明一些计算属性,并使用普通的#if帮助器。

Ember.Handlebars.registerHelper('ifeq', function(a, b, options) {
return Ember.Handlebars.bind.call(options.contexts[0], a, options, true, function(result) {
return result === b;
});
});

你可以这样使用它:

\{\{#ifeq obj.some.property "something"}}
They are equal!
\{\{/ifeq}}

对于那些有比较对象属性的问题,在帮助器中添加这个解决方案

Ember.js helper无法正确识别参数

为了那些生活在边缘的人,把这个提升一个档次。

要点: https://gist.github.com/akhoury/9118682 演示:代码片段如下

把手助手:\{\{#xif EXPRESSION}} \{\{else}} \{\{/xif}}

一个helper来执行带有任意表达式的IF语句

  1. EXPRESSION是一个正确转义的字符串
  2. 是的,你需要正确转义字符串字面量或只是交替单引号和双引号
  3. 你可以访问任何全局函数或属性,例如encodeURIComponent(property)
  4. 这个例子假设你将这个上下文传递给你的手柄template( {name: 'Sam', age: '20' } ),注意agestring,这样我就可以在后面的文章中演示parseInt()

用法:

<p>
\{\{#xif " name == 'Sam' && age === '12' " }}
BOOM
\{\{else}}
BAMM
\{\{/xif}}
</p>

输出

<p>
BOOM
</p>

JavaScript: # EYZ0

 Handlebars.registerHelper("xif", function (expression, options) {
return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});

把手助手:\{\{x EXPRESSION}}

一个执行javascript表达式的助手

  1. EXPRESSION是一个正确转义的字符串
  2. 是的,你需要正确转义字符串字面量或只是交替单引号和双引号
  3. 你可以访问任何全局函数或属性,例如parseInt(property)
  4. 这个例子假设你把这个上下文传递给你的手柄template( {name: 'Sam', age: '20' } )age是一个string用于演示,它可以是任何东西。

用法:

<p>Url: \{\{x "'hi' + name + ', ' + window.location.href + ' <---- this is your href,' + ' your Age is:' + parseInt(this.age, 10)"}}</p>

输出:

<p>Url: hi Sam, http://example.com <---- this is your href, your Age is: 20</p>

JavaScript:

这看起来有点大,因为为了清晰起见,我扩展了语法并注释了几乎每一行

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

如果你想访问上层作用域,这个略有不同,表达式是所有参数的JOIN, 用法:说上下文数据看起来像这样:

// 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);
});

我可以理解为什么你想要创建一个helper的情况下,你有大量不同的比较在你的模板中执行,但相对较少的比较(甚至是一个,这是把我带到这个页面的第一个位置),它可能只是更容易定义一个新的句柄变量在你的视图渲染函数调用,如:

传递到渲染手柄:

var context= {
'section1' : section1,
'section2' : section2,
'section1or2' : (section1)||(section2)
};

然后在handlebars模板中:

\{\{#if section1or2}}
.. content
\{\{/if}}

我提到这一点是为了简单起见,也因为这是一个可能快速且有帮助的答案,同时仍然符合Handlebars的无逻辑性质。

遵循这2个指南a-way-to-let-users-define-custom-made-bound-if-statements自定义绑定助手,我能够调整我在这篇文章中关于stackoverflow的共享视图,以使用这个而不是标准的#if语句。这应该比在那里扔一个#if更安全。

自定义绑定助手在这个要点上是杰出的。

<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>

我正在使用安贝cli项目来构建我的灰烬应用程序。

本文撰写时的当前设置:

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: -------------------------------

下面是我在ember 1.10和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));
});

然后你可以像这样在你的模板中使用它:

// 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}}

其中表达式的参数被传递为p0p1p2等,# EYZ0也可以引用为this

在Ember.js中,你可以在if块助手中使用内联if helper。它可以替换||逻辑运算符,例如:

\{\{#if (if firstCondition firstCondition secondCondition)}}
(firstCondition || (or) secondCondition) === true
\{\{/if}}

句柄支持嵌套操作。如果我们编写的逻辑稍有不同,这就提供了很大的灵活性(和更干净的代码)。

\{\{#if (or section1 section2)}}
.. content
\{\{/if}}

事实上,我们可以添加各种逻辑:

\{\{#if (or
(eq section1 "foo")
(ne section2 "bar"))}}
.. content
\{\{/if}}

只需注册这些helper:

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);
}
});

运行以下命令安装烬真理帮手插件

Ember安装Ember -truth-helpers

您可以开始使用大多数逻辑运算符(eq,not-eq,not,and,or,gt,gte,lt,lte,xor)。

\{\{#if (or section1 section2)}}
...content
\{\{/if}}

你甚至可以包含子表达式来更进一步,

\{\{#if (or (eq section1 "section1") (eq section2 "section2") ) }}
...content
\{\{/if}}

这里我们有多个逻辑&&|| (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

不太确定使用“and”和“or”是否“安全”…也许可以改成“op_and”和“op_or”之类的?

刚从谷歌搜索到这篇文章,关于如何检查一个字符串是否等于另一个字符串。

我在NodeJS服务器端使用HandlebarsJS,但我也在前端使用浏览器版本的HandlebarsJS来解析它。这意味着如果我想要一个自定义助手,我必须在两个不同的地方定义它,或者将一个函数分配给有问题的对象——太费劲了!!

人们忘记了某些对象具有可以在moustache模板中使用的继承函数。对于字符串:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match

# EYZ0

我们可以使用这个方法返回一个匹配数组,如果没有找到匹配则返回null。这是完美的,因为查看HandlebarsJS文档http://handlebarsjs.com/builtin_helpers.html

# EYZ0

所以…

\{\{#if your_string.match "what_youre_looking_for"}}
String found :)
\{\{else}}
No match found :(
\{\{/if}}

# EYZ0

在所有浏览器上测试后,这在Firefox上不起作用. #HandlebarsJS将其他参数传递给函数调用,这意味着当String.prototype.match被调用时,第二个参数(即根据上面的文档,匹配函数调用的Regexp标志)似乎正在被传递。Firefox认为这是不赞成使用String.prototype的。匹配,就这样断了。

一个解决方法是为String JS对象声明一个新的函数原型,并使用它代替:

if(typeof String.includes !== 'function') {
String.prototype.includes = function(str) {
if(!(str instanceof RegExp))
str = new RegExp((str+'').escapeRegExp(),'g');
return str.test(this);
}
}

确保这个JS代码包括之前你运行你的Handlebars.compile()函数,然后在你的模板…

\{\{#your_string}}
\{\{#if (includes "what_youre_looking_for")}}
String found :)
\{\{else}}
No match found :(
\{\{/if}}
\{\{/your_string}}

你可以简单地使用如下所示的逻辑运算符:

\{\{#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}}

在关闭之前,如果你能写出你的业务逻辑

如果你想检查多个条件,这里有一个解决方案:

/* 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);
});

用法:

/* if(list.length>0 && public){}*/


\{\{#checkIf list.length '>' 0 '&&' public '==' true}} <p>condition satisfied</p>\{\{/checkIf}}

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);
});

然后按如下方式调用

\{\{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }}
BTW:注意这里给出的解决方案是不正确的,他没有减去最后一个参数,即函数名。 # EYZ0 < / p >

他最初的AND/OR是基于完整的论证列表

   and: function () {
return Array.prototype.slice.call(arguments).every(Boolean);
},
or: function () {
return Array.prototype.slice.call(arguments).some(Boolean);
}

有人能改一下答案吗?我刚刚浪费了一个小时试图修正一个由86人推荐的答案。修正的是函数名过滤掉最后一个参数。# EYZ0

对于三元helper,还有另一个不正确的解决方案:

'?:' ( condition, first, second ) {
return condition ? first : second;
}


<span>\{\{?: fooExists 'found it' 'nope, sorry'}}</span>

或者一个简单的合并helper:

'??' ( first, second ) {
return first ? first : second;
}


<span>\{\{?? foo bar}}</span>

由于这些字符在句柄标记中没有特殊含义,因此可以自由地将它们用于帮助器名称。

您可以使用以下代码:

\{\{#if selection1}}
doSomething1
\{\{else}}
\{\{#if selection2}}
doSomething2
\{\{/if}}
\{\{/if}}

你不能把你的表达式写在句柄模板中,但你所有的逻辑(表达式)都写在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 -->