在把手JS中是否有办法将逻辑运算符合并到标准handlebars.js条件运算符中?像这样:
{{#if section1 || section2}}
.. content
{{/if}}
我知道我可以写自己的帮手,但首先我要确保我不会重新发明轮子。
答案 0 :(得分:487)
这可以通过使用块助手“作弊”来实现。这可能违背了开发Handlebars的人的意识形态。
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
然后你可以像这样在
中调用模板中的帮助器{{#ifCond v1 v2}}
{{v1}} is equal to {{v2}}
{{else}}
{{v1}} is not equal to {{v2}}
{{/ifCond}}
答案 1 :(得分:424)
进一步解决问题。这会添加比较运算符。
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}}
Coffee Script版本
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
答案 2 :(得分:131)
Handlebars支持嵌套操作。如果我们以稍微不同的方式编写逻辑,这会提供很多灵活性(以及更清晰的代码)。
{{#if (or section1 section2)}}
.. content
{{/if}}
事实上,我们可以添加各种逻辑:
{{#if (or
(eq section1 "foo")
(ne section2 "bar"))}}
.. content
{{/if}}
只需注册这些助手:
Handlebars.registerHelper({
eq: function (v1, v2) {
return v1 === v2;
},
ne: function (v1, v2) {
return v1 !== v2;
},
lt: function (v1, v2) {
return v1 < v2;
},
gt: function (v1, v2) {
return v1 > v2;
},
lte: function (v1, v2) {
return v1 <= v2;
},
gte: function (v1, v2) {
return v1 >= v2;
},
and: function () {
return Array.prototype.slice.call(arguments).every(Boolean);
},
or: function () {
return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
}
});
答案 3 :(得分:87)
要点:https://gist.github.com/akhoury/9118682 演示:下面的代码段
{{#xif EXPRESSION}} {{else}} {{/xif}}
encodeURIComponent(property)
template( {name: 'Sam', age: '20' } )
,请注意age
为string
,因为我可以稍后在此帖子中演示parseInt()
<p>
{{#xif " name == 'Sam' && age === '12' " }}
BOOM
{{else}}
BAMM
{{/xif}}
</p>
<p>
BOOM
</p>
Handlebars.registerHelper("xif", function (expression, options) {
return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});
{{x EXPRESSION}}
parseInt(property)
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>
这看起来有点大,因为为了清晰起见,我扩展了语法并对几乎每一行进行了评论
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>
如果你想要访问上层范围,这个稍有不同,表达式是所有参数的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}}
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);
});
答案 4 :(得分:28)
有一种简单的方法可以在不编写辅助函数的情况下完成此操作......可以在模板中完全完成。
{{#if cond1}}
{{#if con2}}
<div> and condition completed</div>
{{/if}}
{{else}}
<div> both conditions weren't true</div>
{{/if}}
编辑:相反,您可以这样做:
{{#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帮助器有条件地渲染块。如果它是 参数返回false,undefined,null,“”或[](“falsy”值), 那么任何'cond'(如cond1或cond2)都不会被视为真实。
答案 5 :(得分:19)
此处发布的所有答案的一个问题是它们不能使用绑定属性,即当涉及的属性发生更改时,不会重新评估if条件。这是支持绑定的助手的稍微高级版本。它使用Ember源中的bind函数,该函数也用于实现正常的Ember #if
帮助器。
这个限制在左侧的单个绑定属性,与右侧的常量相比,我认为这对于大多数实际目的来说已经足够了。如果您需要比简单比较更先进的东西,那么可能最好开始声明一些计算属性并使用普通#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}}
答案 6 :(得分:13)
改进的解决方案基本上适用于任何二元运算符(至少数字,字符串与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);
}
});
答案 7 :(得分:7)
这是我使用的块助手的链接:comparison block helper。它支持所有标准运算符,并允许您编写如下所示的代码。这真的很方便。
{{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
{{/compare}}
答案 8 :(得分:6)
如果您想检查多个条件,这是一个解决方案:
using Google.Api.Ads.Dfp.v201708;
用法:
/* 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);
});
答案 9 :(得分:4)
与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" numbretwo}}
do something
{{else}}
do something else
{{/compare}}
我建议将对象移出函数以获得更好的性能,否则你可以添加任何你想要的比较函数,包括“和”和“或”。
答案 10 :(得分:3)
通过运行以下命令安装Ember Truth Helpers插件
ember install ember-truth-helpers
你可以开始使用大多数逻辑运算符(eq,not-eq,not,and,或gt,gte,lt,lte,xor)。
{{#if (or section1 section2)}}
...content
{{/if}}
您甚至可以包含子表达式以进一步发展,
{{#if (or (eq section1 "section1") (eq section2 "section2") ) }}
...content
{{/if}}
答案 11 :(得分:3)
另一种选择是在#if
中使用函数名称。 #if
将检测参数是否有效,如果是,那么它将调用它并使用其返回进行真值检查。 myFunction下面的当前上下文为this
。
{{#if myFunction}}
I'm Happy!
{{/if}}
答案 12 :(得分:2)
我可以理解为什么你想要在你的模板中进行大量不同比较的情况下创建一个帮助器,但对于相对较少的比较(甚至一个,这是我带来的)这个页面在第一个位置),在视图渲染函数调用中定义新的把手变量可能更容易,例如:
传递给渲染的把手:
var context= {
'section1' : section1,
'section2' : section2,
'section1or2' : (section1)||(section2)
};
然后在你的车把模板中:
{{#if section1or2}}
.. content
{{/if}}
为了简单起见,我提到这一点,也因为它是一个快速而有用的答案,同时仍然符合Handlebars的无逻辑特性。
答案 13 :(得分:1)
对于那些在比较对象属性时遇到问题的人,在帮助者中添加此解决方案
答案 14 :(得分:1)
不幸的是,这些解决方案都没有解决“OR”运算符“cond1 || cond2”的问题。
使用“^”(或)并检查cond2是否为真
{{#if cond1}} 采取行动 {{^}} {{#if cond2}} 采取行动 {{/如果}} {{/如果}}
它打破了DRY规则。那么为什么不使用partial来减少它的混乱
{{#if cond1}}
{{> subTemplate}}
{{^}}
{{#if cond2}}
{{> subTemplate}}
{{/if}}
{{/if}}
答案 15 :(得分:1)
如果您只是想检查是否存在一个或另一个元素,则可以使用此自定义帮助程序
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}}
如果您还需要能够进行“或”比较 function return values 我宁愿添加另一个返回所需结果的属性。
毕竟模板应该是无逻辑的!
答案 16 :(得分:1)
这里我们有多个逻辑&amp;&amp; amp;&amp; amp;&amp;和|| (和或):
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
不确定使用“和”和“或”是否“安全”......可能会改为“op_and”和“op_or”?
答案 17 :(得分:1)
刚从谷歌搜索来看这篇文章如何检查一个字符串是否等于另一个字符串。
我在NodeJS服务器端使用HandlebarsJS,但我也使用HandlebarsJS的浏览器版本在前端使用相同的模板文件来解析它。这意味着如果我想要一个自定义帮助器,我必须在两个不同的位置定义它,或者为相关对象分配一个函数 - 太费力了!
人们忘记的是某些对象具有可以在胡子模板中使用的继承函数。如果是字符串:
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.
我们可以使用此方法返回匹配数组,如果未找到匹配项,则返回null
。这很完美,因为查看HandlebarsJS文档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.
因此...
{{#if your_string.match "what_youre_looking_for"}}
String found :)
{{else}}
No match found :(
{{/if}}
更新:
在所有浏览器上测试后,这对Firefox不起作用。 HandlebarsJS将其他参数传递给函数调用,这意味着当调用String.prototype.match时,第二个参数(即上述文档中匹配函数调用的Regexp标志)似乎正在传递。 Firefox认为这是对String.prototype.match的弃用,因此会中断。
解决方法是为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);
}
}
确保在运行Handlebars.compile()函数之前 之前包含此JS代码,然后在模板中
{{#your_string}}
{{#if (includes "what_youre_looking_for")}}
String found :)
{{else}}
No match found :(
{{/if}}
{{/your_string}}
答案 18 :(得分:1)
我找到了一个用CoffeeScript制作的npm包,它为Handlebars提供了许多令人难以置信的有用助手。请查看以下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"}}
答案 19 :(得分:1)
正确解决AND / OR
abba.flatMap{ case (c, i) => for (j <- 1 to i) yield List((c, j)) }
// res2: List[List[(Char, Int)]] = List(List((a,1)), List((a,2)), List((b,1)))
然后如下呼叫
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);
});
BTW:请注意,此处给出的解决方案不正确,他没有减去最后一个参数,即函数名称。 https://stackoverflow.com/a/31632215/1005607
他的原始AND / OR基于完整的参数列表
{{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }}
有人可以改变这个答案吗?我只是浪费了一个小时试图解决86人推荐的答案。修复是过滤掉最后一个参数,即函数名称。 and: function () {
return Array.prototype.slice.call(arguments).every(Boolean);
},
or: function () {
return Array.prototype.slice.call(arguments).some(Boolean);
}
答案 20 :(得分:0)
按照这两个指南a-way-to-let-users-define-custom-made-bound-if-statements和custom bound helpers,我可以在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>
我正在使用ember cli项目来构建我的ember应用程序。
此帖发布时的当前设置:
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: -------------------------------
答案 21 :(得分:0)
在Ember.js中,你可以在if block helper中使用inline if helper。它可以替换||
逻辑运算符,例如:
{{#if (if firstCondition firstCondition secondCondition)}}
(firstCondition || (or) secondCondition) === true
{{/if}}
答案 22 :(得分:0)
您只需使用如下所示的逻辑运算符即可:
{{#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}}
关闭之前,如果您可以编写业务逻辑
答案 23 :(得分:0)
三元助手的另一个错误解决方案:
'?:' ( condition, first, second ) {
return condition ? first : second;
}
<span>{{?: fooExists 'found it' 'nope, sorry'}}</span>
或简单的合并助手:
'??' ( first, second ) {
return first ? first : second;
}
<span>{{?? foo bar}}</span>
由于这些角色在手柄标记中没有特殊含义,因此您可以自由地将它们用于帮助名称。
答案 24 :(得分:0)
您可以使用以下代码:
{{#if selection1}}
doSomething1
{{else}}
{{#if selection2}}
doSomething2
{{/if}}
{{/if}}
答案 25 :(得分:-1)
这是我用于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}}
表达式的参数作为p0
传递,p1
,p2
等,p0
也可以引用为this
。