Logical operator in a handlebars.js {{#if}} conditional
handlebars JS中是否可以将逻辑运算符合并到标准handlebars.js条件运算符中? 像这样:
1 2 3 | {{#if section1 || section2}} .. content {{/if}} |
我知道我可以编写自己的帮助程序,但是首先我想确保自己不会重新发明轮子。
这可以通过与辅助助手"作弊"来实现。这可能与开发把手的人的思想背道而驰。
1 2 3 4 5 6 | Handlebars.registerHelper('ifCond', function(v1, v2, options) { if(v1 === v2) { return options.fn(this); } return options.inverse(this); }); |
然后,您可以像这样在模板中调用帮助程序
1 2 3 4 5 | {{#ifCond v1 v2}} {{v1}} is equal to {{v2}} {{else}} {{v1}} is not equal to {{v2}} {{/ifCond}} |
使解决方案更进一步。这将添加比较运算符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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); } }); |
在这样的模板中使用它:
1 | {{#ifCond var1 '==' var2}} |
Coffee Script版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
车把支持嵌套操作。如果我们将逻辑编写得有些不同,这将提供很大的灵活性(和更简洁的代码)。
1 2 3 | {{#if (or section1 section2)}} .. content {{/if}} |
实际上,我们可以添加各种逻辑:
1 2 3 4 5 | {{#if (or (eq section1"foo") (ne section2"bar"))}} .. content {{/if}} |
只需注册这些助手:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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); } }); |
对于那些生活在边缘的人来说,这是一个不错的选择。
要点:https://gist.github.com/akhoury/9118682
演示:下面的代码段
车把助手:
一个可以执行带有任何表达式的IF语句的助手
用法:
1 2 3 4 5 6 7 8 9 | <p> {{#xif" name == 'Sam' && age === '12'" }} BOOM {{else}} BAMM {{/xif}} </p> |
输出量
1 2 3 4 5 | <p> BOOM </p> |
JavaScript :(取决于另一个助手-继续阅读)
1 2 3 | Handlebars.registerHelper("xif", function (expression, options) { return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this); }); |
车把帮手:
执行javascript表达式的助手
用法:
1 2 3 | <p> Url: {{x"'hi' + name + ', ' + window.location.href + ' <---- this is your href,' + ' your Age is:' + parseInt(this.age, 10)"}} </p> |
输出:
1 2 3 | <p> Url: hi Sam, http://example.com <---- this is your href, your Age is: 20 </p> |
JavaScript:
这看起来有点大,因为我扩展了语法并为清晰起见在几乎每一行都加了注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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 + '\'}} ?JS-Error: ', e, ' ?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: '[email protected]' }, { firstName: 'Sam', age: '18', email: '[email protected]' }, { firstName: 'Perter', lastName: 'Smith', age: '25', email: '[email protected]' }]; var source = $("#template").html(); var template = Handlebars.compile(source); $("#main").html(template(data)); |
1 2 3 4 5 6 7 8 9 10 11 | h1 { font-size: large; } .content { padding: 10px; } .person { padding: 5px; margin: 5px; border: 1px solid grey; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> <script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js"> <script id="template" type="text/x-handlebars-template"> {{#each this}} {{x "'Hi ' + firstName"}}, {{x 'lastName'}} {{x '"you were born in" + ((new Date()).getFullYear() - parseInt(this.age, 10)) '}} {{#xif 'parseInt(age) >= 21'}} login here: http://foo.bar?email={{x 'encodeURIComponent(email)'}} {{else}} Please go back when you grow up. {{/xif}} {{/each}} |
磨牙
如果您要访问上级作用域,则此操作略有不同,表达式是所有参数的JOIN,
用法:说上下文数据看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 | // 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:
1 2 3 4 5 6 7 8 9 10 11 | 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); }); |
有一种简单的方法无需编写辅助函数即可完成此操作...可以完全在模板中完成。
1 2 3 4 5 6 7 | {{#if cond1}} {{#if con2}} and condition completed {{/if}} {{else}} both conditions weren't true {{/if}} |
编辑:相反,您可以通过执行以下操作:
1 2 3 4 5 6 7 8 9 | {{#if cond1}} or condition completed {{else}} {{#if cond2}} or condition completed {{else}} neither of the conditions were true {{/if}} {{/if}} |
编辑/注意:从车把的网站:handlebarsjs.com,这里是虚假的值:
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.
这里发布的所有答案的一个问题是它们不适用于绑定的属性,即,当涉及的属性发生变化时,if条件不会重新评估。这是支持绑定的助手的稍微高级的版本。它使用Ember源中的bind函数,该函数还用于实现普通的Ember
与右侧的常量相比,此常量仅限于左侧的单一绑定属性,我认为这对于大多数实际用途而言已经足够。如果您需要比简单比较更高级的功能,那么最好开始声明一些计算的属性并使用常规的
1 2 3 4 5 | Ember.Handlebars.registerHelper('ifeq', function(a, b, options) { return Ember.Handlebars.bind.call(options.contexts[0], a, options, true, function(result) { return result === b; }); }); |
您可以像这样使用它:
1 2 3 | {{#ifeq obj.some.property"something"}} They are equal! {{/ifeq}} |
改进的解决方案,基本上可以与任何二进制运算符一起使用(至少数字,字符串不能与eval一起很好地使用,如果使用带有用户输入的未定义的运算符,请注意可能的脚本注入):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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); } }); |
如果要检查多个条件,请使用以下解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /* 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); }); |
用法:
1 2 3 4 5 | /* if(list.length>0 && public){}*/ {{#checkIf list.length '>' 0 '&&' public '==' true}} <p> condition satisfied </p>{{/checkIf}} |
这是我使用的块帮助器的链接:比较块帮助器。它支持所有标准运算符,并允许您编写如下所示的代码。真的很方便。
1 2 3 | {{#compare Database.Tables.Count">" 5}} There are more than 5 tables {{/compare}} |
与Jim的答案类似,但是通过使用一些创造力,我们还可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 ); } ); |
然后使用它,我们得到如下内容:
1 2 3 4 5 | {{#compare numberone"eq" numbretwo}} do something {{else}} do something else {{/compare}} |
我建议将对象移出该函数,以获得更好的性能,但否则,您可以添加所需的任何比较函数,包括" and"和" or"。
通过运行以下命令来安装Ember Truth Helpers插件
ember install ember-truth-helpers
您可以开始使用大多数逻辑运算符(eq,not-eq,not和and,gt,gte,lt,lte,xor)。
1 2 3 | {{#if (or section1 section2)}} ...content {{/if}} |
您甚至可以包含子表达式以进一步扩展,
1 2 3 | {{#if (or (eq section1"section1") (eq section2"section2") ) }} ...content {{/if}} |
另一种选择是在
1 2 3 | {{#if myFunction}} I'm Happy! {{/if}} |
不幸的是,这些解决方案都不能解决" OR"运算符" cond1 || cond2"的问题。
使用" ^"(或)并检查cond2是否为true
{{## cond1}}
采取行动
{{^}}
{{#if cond2}}
采取行动
{{/如果}}
{{/如果}}
它违反了DRY规则。那么为什么不使用partial来减少混乱
1 2 3 4 5 6 7 | {{#if cond1}} {{> subTemplate}} {{^}} {{#if cond2}} {{> subTemplate}} {{/if}} {{/if}} |
我能理解为什么您要为模板中有大量要执行的各种比较的情况创建助手,而对于比较少的比较(甚至是一个比较,这就是使我进入此页面的原因)的原因首先),在视图渲染函数调用中定义新的handlebars变量可能会更容易,例如:
传递到渲染上的车把:
1 2 3 4 5 | var context= { 'section1' : section1, 'section2' : section2, 'section1or2' : (section1)||(section2) }; |
然后在您的车把模板中:
1 2 3 | {{#if section1or2}} .. content {{/if}} |
我之所以提及这一点是为了简单起见,也是因为它是一个快速且有用的答案,同时仍然符合Handlebars的无逻辑本质。
三元助手的另一个弯曲的解决方案:
1 2 3 4 5 | '?:' ( condition, first, second ) { return condition ? first : second; } <span>{{?: fooExists 'found it' 'nope, sorry'}}</span> |
或一个简单的合并助手:
1 2 3 4 5 | '??' ( first, second ) { return first ? first : second; } <span>{{?? foo bar}}</span> |
由于这些字符在车把标记中没有特殊含义,因此您可以随意使用它们作为助手名称。
在这里,我们有用于多个逻辑&&和||的香草车把。 (和):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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"之类的东西?
如果您只想检查一个或另一个元素是否存在,则可以使用此自定义帮助器
1 2 3 4 5 6 7 | 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); } }); |
像这样
1 2 3 4 5 | {{#if_or elem1 elem2}} {{elem1}} or {{elem2}} are present {{else}} not present {{/if_or}} |
如果您还需要能够使用"或"进行比较
函数返回值
我宁愿添加另一个返回所需结果的属性。
毕竟模板应该没有逻辑!
刚刚从Google搜索中找到有关如何检查一个字符串是否等于另一个字符串的帖子。
我在NodeJS服务器端使用HandlebarsJS,但是我也使用浏览器版本的HandlebarsJS在前端使用相同的模板文件进行解析。这意味着,如果我想要一个自定义帮助器,则必须在2个不同的地方定义它,或者为有问题的对象分配一个功能-太费力了!
人们忘记的是某些对象具有可在小胡子模板中使用的继承功能。如果是字符串:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/match
我们可以使用此方法返回匹配项数组,如果未找到匹配项,则返回
所以...
1 2 3 4 5 | {{#if your_string.match"what_youre_looking_for"}} String found :) {{else}} No match found :( {{/if}} |
更新:
在所有浏览器上进行测试之后,这在Firefox上不起作用。 HandlebarsJS将其他参数传递给函数调用,这意味着在调用String.prototype.match时,似乎正在传递第二个参数(即,根据上述文档,match函数调用的Regexp标志)。 Firefox将其视为String.prototype.match的已弃用,因此会中断。
一种解决方法是为String JS对象声明一个新的函数原型,并使用该原型:
1 2 3 4 5 6 7 | 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代码,然后在模板中...
1 2 3 4 5 6 7 | {{#your_string}} {{#if (includes"what_youre_looking_for")}} String found :) {{else}} No match found :( {{/if}} {{/your_string}} |
我发现了使用CoffeeScript制作的npm软件包,其中有许多令人难以置信的有用的辅助工具。在以下URL中查看文档:
https://npmjs.org/package/handlebars-helpers
您可以执行
您将能够执行类似
正确的AND / OR解决方案
1 2 3 4 5 6 7 8 | 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); }); |
然后调用如下
1 | {{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }} |
顺便说一句:请注意这里给出的解决方案是不正确的,他没有减去最后一个参数,即函数名。
https://stackoverflow.com/a/31632215/1005607
他最初的AND / OR是基于完整的参数列表
1 2 3 4 5 6 | and: function () { return Array.prototype.slice.call(arguments).every(Boolean); }, or: function () { return Array.prototype.slice.call(arguments).some(Boolean); } |
有人可以更改答案吗?我只是浪费了一个小时,试图修复86个人推荐的答案中的某些内容。解决方法是过滤掉最后一个参数,即函数名。
对于那些在比较对象属性方面遇到问题的人,可以在帮助器内部添加此解决方案
Ember.js帮助器无法正确识别参数
按照这两个指南,让用户定义自定义绑定if语句和自定义绑定帮助器,我能够在stackoverflow上的这篇文章中调整我的共享视图,以使用它代替标准的#如果声明。这不仅比在其中扔一个#if更安全。
该要旨中的自定义绑定助手非常出色。
1 2 3 4 5 6 7 8 | <li> {{#if-equal view.showDiv"true"}}{{/if-equal}}<i class="fa fa-rss-square">{{#if-equal view.showDiv"true"}}{{/if-equal}} {{#if-equal view.showTitle"true"}}Blog{{/if-equal}} </li> |
我正在使用ember cli项目来构建我的ember应用程序。
撰写本文时的当前设置:
1 2 3 4 5 6 | 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.js中,可以在if块帮助程序中使用内联if帮助程序。它可以替换
1 2 3 | {{#if (if firstCondition firstCondition secondCondition)}} (firstCondition || (or) secondCondition) === true {{/if}} |
您可以简单地通过使用如下所示的逻辑运算符来做到这一点:
1 2 3 | {{#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}} |
在关闭之前,如果可以编写业务逻辑
这是我用于ember 1.10和ember-cli 2.0的方法。
1 2 3 4 5 6 | // 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)); }); |
然后,您可以在模板中使用它,如下所示:
1 2 3 4 5 6 7 8 | // 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}} |
表达式的参数以