Логический оператор в handlebars.js {{#if}} условный

Есть ли способ в дескрипторах JS включить логические операторы в стандартный условный оператор handlebars.js? Что-то вроде этого:

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

Я знаю, что могу написать своего помощника, но сначала я хотел бы убедиться, что я не изобретаю колесо.

Ответ 1

Это возможно путем "обмана" с помощью вспомогательного блока. Это, вероятно, идет вразрез с идеологией людей, которые разработали 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}}

Ответ 2

Принимая решение на один шаг дальше. Это добавляет оператор сравнения.

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

Кофе 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

Ответ 3

взяв это за ступеньку, для тех из вас, кто живет на краю.

gist: https://gist.github.com/akhoury/9118682 Демо: фрагмент кода ниже

Помощник ручек: {{#xif EXPRESSION}} {{else}} {{/xif}}

помощник для выполнения оператора IF с любым выражением

  • EXPRESSION - это правильно экранированная строка
  • Да, вы НУЖДЫ, чтобы правильно избежать строковых литералов или просто чередовать одиночные и двойные кавычки
  • вы можете получить доступ к любой глобальной функции или свойству, т.е. encodeURIComponent(property)
  • В этом примере предполагается, что вы передали этот контекст вашим дескрипторам 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

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

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

Ответ 4

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 (v1, v2) {
        return v1 && v2;
    },
    or: function (v1, v2) {
        return v1 || v2;
    }
});

Ответ 5

Существует простой способ сделать это без написания вспомогательной функции... Это можно сделать в шаблоне полностью.

{{#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, "или [] (значение" ложь "), Тогда любой" cond" (например, cond1 или cond2) не будет считаться истинным.

Ответ 6

Одна проблема со всеми ответами, размещенными здесь, заключается в том, что они не работают со связанными свойствами, то есть условие if не переоценивается, когда изменяемые свойства изменяются. Здесь немного более продвинутая версия вспомогательных привязок хелпера. Он использует функцию bind из источника Ember, которая также используется для реализации обычного помощника 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}}

Ответ 7

Улучшенное решение, которое в основном работает с любым двоичным оператором (по крайней мере, числа, строки не работают с eval, УБЕДИТЕСЬ ВОЗМОЖНО SCRIPT ИНЪЕКЦИЯ, ЕСЛИ ИСПОЛЬЗУЕТ НЕ ОПРЕДЕЛЕННЫЙ ОПЕРАТОР С ВХОДАМИ ПОЛЬЗОВАТЕЛЯ):

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

Ответ 8

Здесь ссылка на вспомогательный блок, который я использую: вспомогательный блок сравнения. Он поддерживает все стандартные операторы и позволяет писать код, как показано ниже. Это действительно очень удобно.

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

Ответ 9

Подобно тому, как Джим отвечает, но используя немного творчества, мы могли бы также сделать что-то вроде этого:

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

Еще одна альтернатива - использовать имя функции в #if. #if будет определять, является ли параметр функцией, и если он будет тогда, он будет вызывать его и использовать его возврат для проверки правдоподобия. Ниже myFunction получает текущий контекст как this.

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

Ответ 11

Установите Помощник Ember Truth Helpers, выполнив следующую команду

ember install 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}}

Ответ 12

Я могу понять, почему вы хотите создать помощник для ситуаций, когда у вас есть большое количество разнообразных сравнений для выполнения в вашем шаблоне, но для относительно небольшого количества сравнений (или даже одного, что и привело меня к эта страница в первую очередь), вероятно, было бы проще определить новую переменную handlebars в вызове функции рендеринга представления, например:

Перейдите к рулям в рендере:

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

а затем в шаблоне handlebars:

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

Я упомянул об этом ради простоты, а также потому, что это ответ, который может быть быстрым и полезным, но при этом соблюдая логическую природу Handlebars.

Ответ 13

Вот решение, если вы хотите проверить несколько условий:

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

Ответ 14

Я нашел пакет npm, сделанный с CoffeeScript, который имеет много невероятных полезных помощников для 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"}}

Ответ 15

если вы просто хотите проверить, присутствует ли тот или иной элемент, вы можете использовать этот настраиваемый помощник

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

если вам также нужно иметь "или", чтобы сравнить возвращаемые значения функции  Я предпочел бы добавить другое свойство, которое возвращает желаемый результат.

Шаблоны должны быть абсолютно безболезненными!

Ответ 16

К сожалению, ни одно из этих решений не решает проблему оператора "OR" cond1 || cond2 ".

  • Проверьте, истинно ли первое значение
  • Используйте "^" (или) и проверьте, соответствует ли другое cond2

    {{# if cond1}}   ДЕЙСТВИЕ {{^}}   {{#if cond2}}       ДЕЙСТВИЕ   {{/если}} {{/, Если}}

Это нарушает правило DRY. Так почему бы не использовать частичное, чтобы сделать его менее грязным

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

Ответ 18

Здесь у нас есть ванильные рули для множества логических && и || (и/или):

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.fn(this);
        }
    }

    return options.inverse(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"?

Ответ 19

Только что пришел к этому сообщению из поиска Google, как проверить, равна ли строка другой строке.

Я использую HandlebarsJS на стороне сервера NodeJS, но я также использую те же файлы шаблонов в интерфейсе, используя версию браузера 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);
    }
}

Убедитесь, что этот JS-код включен до, вы запустите свою функцию Handlebars.compile(), затем в своем шаблоне...

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

Ответ 20

Следуя этим двум направляющим a-way-to-let-users-define-custom-made-bound-if-statements и настраиваемые связанные помощники Я смог настроить общие представления в этом сообщении на fooobar.com/questions/18135/..., чтобы использовать это вместо стандартного оператора #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

В Ember.js вы можете использовать встроенный, если помощник в блочном помощнике. Он может заменить логический оператор ||, например:

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

Ответ 22

Вы можете сделать это просто, используя логический оператор, как показано ниже:

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

Вот подход, который я использую для 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.