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

IT技术 javascript handlebars.js
2021-02-09 03:07:18

在handlebars JS 中有没有办法将逻辑运算符合并到标准handlebars.js 条件运算符中?像这样的东西:

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

我知道我可以编写自己的助手,但首先我想确保我没有重新发明轮子。

6个回答

这可以通过使用块助手“作弊”来实现。这可能与开发 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 / Moustache 的无逻辑性质背道而驰,但仍然很有用,谢谢!
2021-03-13 03:07:18
@BalaClark 真的没有逻辑吗?我的意思是它仍然有“if”语句——仅仅因为它们被故意残废并不会改变哲学。
2021-03-16 03:07:18
请注意,这根本不适用于绑定属性(读取、Ember 绑定)。文字值很好,但不能解析模型属性(使用 Ember 1.0.0-rc.8 和 Handlebars 1.0.0 测试),并且registerBoundHelper无法处理 Handlebars 语法。解决方法是创建一个自定义视图:stackoverflow.com/questions/18005111/...
2021-03-26 03:07:18
我不明白为什么 AND / OR 不能成为 Handlebars 的一部分。它不违背无逻辑的性质,因为已经有 IF、UNLESS 和 EACH ......对于无逻辑来说这么多
2021-04-03 03:07:18
@StackOverflowed 限制您的选择/限制自己有助于遵循开发理念。凭借有效的开发理念,当您尝试解决它旨在解决的问题时,实现它的 API 不应该感到有障碍。相反,它应该以一种奇怪的方式感觉授权:)(有点像禁欲)。回到主题,要么 Handlebars 哲学是矛盾的,这个问题中描述的问题可以通过一个解决方案来解决,handlebars API 实现得不好,或者这不是 handlebars 旨在解决的问题。
2021-04-09 03:07:18

将解决方案更进一步。这添加了比较运算符。

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
我发现不评估对象属性的值。添加以下帮助 v1 = Ember.Handlebars.get(this, v1, options) v2 = Ember.Handlebars.get(this, v2, options)
2021-03-12 03:07:18
不要忘记'||''&&'我添加了这些案例,它们非常有用。
2021-03-21 03:07:18
如果有这个,你能做别的吗?
2021-03-22 03:07:18
但是如果 v1 和 v2 也是一个条件,那么我该如何使用?例如: if(value == "a" || value == "b")
2021-04-02 03:07:18
作为把手的菜鸟,有一件不清楚的事情是您必须将运算符作为字符串传递,否则编译器在标记模板时会出错。{{#ifCond 真 '==' 假}}
2021-04-09 03:07:18

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);
    }
});
一个更高级的版本,可以在每个运算符上使用多个参数。
2021-03-11 03:07:18
我们似乎已经将 Lisp 构建到了 Handlebars 中。
2021-03-17 03:07:18
andor助手只使用 2 个参数,这不是您想要的。我设法对 theandorhelpers 进行了重新设计以支持两个以上的参数。将此单衬用于and:return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);并将此单衬用于or: return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);
2021-03-20 03:07:18
请注意,这在 Ember 1.10 中的 HTMLBars 之前是不可能的。此外,如果您愿意,这些助手可以作为 Ember CLI 插件出现:github.com/jmurphyau/ember-truth-helpers
2021-03-26 03:07:18
这样,没有options执行更好地与if方法保持一致并且更容易测试。谢谢!
2021-04-07 03:07:18

对于那些生活在边缘的人来说,把这个提升一个档次。

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

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

使用任何表达式执行 IF 语句的助手

  1. EXPRESSION 是一个正确转义的字符串
  2. 是的,您需要正确转义字符串文字或仅交替使用单引号和双引号
  3. 您可以访问任何全局函数或属性,即 encodeURIComponent(property)
  4. 这个例子假设你把这个上下文传递给你的把手template( {name: 'Sam', age: '20' } ),注意age是一个string,只是为了我可以parseInt()在这篇文章的后面进行演示

用法:

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

输出

<p>
  BOOM
</p>

JavaScript:(这取决于另一个助手-继续阅读)

 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>

摩尔

如果你想访问上层范围,这个略有不同,表达式是所有参数的 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);
});
找到了“上限访问”的解决方案,但使用了新的助手,请参阅更新的答案,{{z ...}}助手
2021-03-11 03:07:18
对于 NodeJS 使用,您可能希望使用更安全的 eval 解决方案,例如:npmjs.com/package/safe-eval 而不是 eval() 函数
2021-03-17 03:07:18
@rickysullivan 尝试eval(expression);用这个替换你的eval('(function(){try{return ' + expression + ';}catch(e){}})();');- 我知道这越来越难看,但想不出一种安全的方法来做到这一点 - 请注意,这不是“快速”执行,我不会这样做巨大的迭代。
2021-03-27 03:07:18
:) 谢谢,我稍微更新了要点以添加更多好东西(与这个 SO 问题没有直接关系 - 但本着在车把模板中做你想做的事情的精神)但是,你应该注意限制,我没有没有找到从表达式中访问“上层范围级别”的方法,假设您在每个范围内,{{#each}} ... {{xif ' ../name === this.name' }} {{/each}}...但我仍在调查..
2021-03-30 03:07:18
这很棒。车把不能告诉你该怎么做:)
2021-04-08 03:07:18

有一种简单的方法可以在不编写辅助函数的情况下执行此操作......它可以完全在模板中完成。

{{#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)都不会被视为真。

这不能正常工作。如果 cond1 为真而 con2 为假,则不会打印任何内容。
2021-03-11 03:07:18
不是真的,你不比较两个值,你只是确保它们都存在,它是不同的。
2021-03-14 03:07:18
你是对的,我首先认为测试是为了比较两个值,而不是对两者进行测试。我的不好,对不起。
2021-03-20 03:07:18
实际上,它以与 javascript 相同的方式对其进行评估,来自网站:“您可以使用 if 帮助程序有条件地呈现块。如果其参数返回 false、undefined、null、””或 [ ](“falsy”值),把手不会渲染块。”
2021-03-27 03:07:18
嘿@TessaLau,我想我知道你来自哪里。但是,如果您绘制控制流,您会看到在第一行中将控制流移动到该条件。
2021-04-07 03:07:18