[英]Logical operator in a handlebars.js {{#if}} conditional
handlebars JS 中是否有一種方法可以將邏輯運算符合並到標准 handlebars.js 條件運算符中? 像這樣:
{{#if section1 || section2}}
.. content
{{/if}}
我知道我可以編寫自己的助手,但首先我想確保我沒有重新發明輪子。
這可以通過使用塊助手“作弊”來實現。 這可能與開發 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}}
將解決方案更進一步。 這添加了比較運算符。
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
Handlebars 支持嵌套操作。 如果我們以稍微不同的方式編寫我們的邏輯,這會提供很大的靈活性(和更清晰的代碼)。
{{#if (or section1 section2)}}
.. content
{{/if}}
其實我們可以添加各種邏輯:
{{#if (or
(eq section1 "foo")
(ne section2 "bar"))}}
.. content
{{/if}}
只需注冊這些助手:
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);
}
});
對於那些生活在邊緣的人來說,把這個提升一個檔次。
要點: https : //gist.github.com/akhoury/9118682演示:下面的代碼片段
{{#xif EXPRESSION}} {{else}} {{/xif}}
助手: {{#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);
});
有一種簡單的方法可以在不編寫輔助函數的情況下執行此操作......它可以完全在模板中完成。
{{#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)都不會被視為真。
此處發布的所有答案的一個問題是它們不適用於綁定屬性,即當所涉及的屬性發生更改時不會重新評估 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}}
改進的解決方案基本上適用於任何二元運算符(至少數字、字符串不適用於 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);
}
});
如果您想檢查多個條件,這是一個解決方案:
/* 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}}
這是我使用的塊助手的鏈接: 比較塊助手。 它支持所有標准運算符,並允許您編寫如下所示的代碼。 這真的很方便。
{{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
{{/compare}}
類似於吉姆的回答,但使用一點創造力,我們也可以做這樣的事情:
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}}
我建議將對象移出函數以獲得更好的性能,否則您可以添加所需的任何比較函數,包括“and”和“or”。
通過運行以下命令安裝Ember Truth Helpers插件
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}}
三元助手的另一個錯誤解決方案:
'?:' ( condition, first, second ) {
return condition ? first : second;
}
<span>{{?: fooExists 'found it' 'nope, sorry'}}</span>
或者一個簡單的合並助手:
'??' ( first, second ) {
return first ? first : second;
}
<span>{{?? foo bar}}</span>
由於這些字符在車把標記中沒有特殊含義,因此您可以自由地將它們用作助手名稱。
另一種選擇是在#if
使用函數名稱。 #if
將檢測參數是否為函數,如果是,則調用它並使用其返回值進行真實性檢查。 在 myFunction 下面獲取當前上下文作為this
。
{{#if myFunction}}
I'm Happy!
{{/if}}
不幸的是,這些解決方案都沒有解決“或”運算符“cond1 || cond2”的問題。
使用“^”(或)並檢查 cond2 是否為真
{{#if cond1}} 做動作 {{^}} {{#if cond2}} 做動作 {{/if}} {{/if}}
它打破了 DRY 規則。 那么為什么不使用部分來使它不那么凌亂
{{#if cond1}}
{{> subTemplate}}
{{^}}
{{#if cond2}}
{{> subTemplate}}
{{/if}}
{{/if}}
我可以理解為什么您要為在模板中執行大量不同比較的情況創建一個幫助程序,但要進行相對較少的比較(甚至是一個比較,這就是將我帶到此頁面的原因)首先),在視圖渲染函數調用中定義一個新的 handlebars 變量可能會更容易,例如:
在渲染時傳遞到把手:
var context= {
'section1' : section1,
'section2' : section2,
'section1or2' : (section1)||(section2)
};
然后在您的把手模板中:
{{#if section1or2}}
.. content
{{/if}}
我提到這一點是為了簡單起見,也因為它是一個快速且有用的答案,同時仍然符合 Handlebars 的無邏輯性質。
我發現了一個用 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"}}
如果您只想檢查一個或另一個元素是否存在,您可以使用此自定義助手
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}}
如果您還需要能夠使用“或”來比較函數返回值,我寧願添加另一個返回所需結果的屬性。
畢竟模板應該是沒有邏輯的!
對於那些在比較對象屬性時遇到問題的人,請在助手中添加此解決方案
這里我們有用於多個邏輯 && 和 || 的普通把手 (和或):
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 的瀏覽器版本來解析它。 這意味着如果我想要一個自定義助手,我必須在 2 個不同的地方定義它,或者為有問題的對象分配一個函數 - 太費力了!!
人們忘記的是某些對象具有可以在 mustache 模板中使用的繼承函數。 在字符串的情況下:
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 object 聲明一個新的功能原型,並使用它:
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}}
您只需使用如下所示的邏輯運算符即可完成此操作:
{{#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}}
在關閉之前,如果您可以編寫業務邏輯
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') (..) ) }}
順便說一句:請注意,這里給出的解決方案是不正確的,他沒有減去最后一個參數,即函數名稱。 https://stackoverflow.com/a/31632215/1005607
他最初的 AND/OR 是基於完整的參數列表
and: function () {
return Array.prototype.slice.call(arguments).every(Boolean);
},
or: function () {
return Array.prototype.slice.call(arguments).some(Boolean);
}
有人可以更改該答案嗎? 我只是浪費了一個小時試圖修復 86 人推薦的答案中的某些內容。 解決方法是過濾掉最后一個參數,即函數名稱。 Array.prototype.slice.call(arguments, 0, arguments.length - 1)
遵循這兩個指南a-way-to-let-users-define-custom-made-bound-if-statements和custom bound -if-statements和自定義綁定助手,我能夠在stackoverflow上的這篇文章中調整我的共享視圖以使用它而不是標准#如果語句。 這應該比只是在那里扔一個 #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: -------------------------------
在 Ember.js 中,您可以在 if 塊助手中使用內聯 if助手。 它可以代替||
邏輯運算符,例如:
{{#if (if firstCondition firstCondition secondCondition)}}
(firstCondition || (or) secondCondition) === true
{{/if}}
您可以使用以下代碼:
{{#if selection1}}
doSomething1
{{else}}
{{#if selection2}}
doSomething2
{{/if}}
{{/if}}
您不能在車把模板中編寫表達式,而是在 express.js 中編寫所有邏輯(表達式)
應用程序.js<\/strong>
res.render("view.hbs", {expression: section1 || section2})
這是我用於 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
引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.