Шаблонизатор Handlebars для JavaScript предоставляет условный оператор if (так же возможно использование и else), но оператор if может обрабатывать только единственное значение (не выражение). Вы можете написать

{{#if name}}
..
{{/if}}

но не можете написать

{{#if name == 'Foo'}}
..
{{/if}}

Давайте создадим хелпер (обработчик) для Handlebars, который будет предоставлять такую функциональность.

Условие if

Но перед тем, как создавать хелпер, давайте посмотрим на полноценный пример с использованием оператора if. В объекте есть два значения: cond1 и cond2, true и false соответственно. Остальная часть кода это просто получение шаблона и обработка данных в Handlebars.

examples/js/handlebars_if.js

var data = {
   'cond1'  : true,
   'cond2'  : false,
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

Шаблон имеет два фрагмента типа этого, каждый из которых используется для своей переменной.

{{#if cond1}}
    true
{{else}}
    false
{{/if}}

Полный html код такой:

examples/js/handlebars_if.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if cond1 (expected true)
    {{#if cond1}}
       true
    {{else}}
      false
    {{/if}}

    <br>#if cond2 (expected false)
    {{#if cond2}}
      true
    {{else}}
      false
    {{/if}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>


Try!

Вы можете посмотреть его работу, нажав на ссылку "Try" (откроется в новом окне). Кнопка "show" запускает обработку.

if_eq

В следующем примере мы реализуем хелпер Handlebars с именем if_eq. Он ожидает два параметра и будет сравнивать их с помощью ==. Хелпер выглядит вот так:

Handlebars.registerHelper('if_eq', function(a, b, opts) {
    if (a == b) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});

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

{{#if_eq name 'Foo'}}
      true
{{else}}
      false
{{/if_eq}}

Файл с JavaScript также содержит объект с данными и код, который у нас был раньше (объединяющий шаблон и данные):

examples/js/handlebars_helpers_if_eq.js

var data = {
   'name'   : 'Foo',
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

Handlebars.registerHelper('if_eq', function(a, b, opts) {
    if (a == b) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});

Весь HTML, включая шаблон, выглядит так:

examples/js/handlebars_helpers_if_eq.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if_eq name Foo (expected true)
    {{#if_eq name 'Foo'}}
      true
    {{else}}
      false
    {{/if_eq}}
    
    <br>#if_eq name 'Bar' (expected false)
    {{#if_eq name 'Bar'}}
       true
    {{else}}
       false
    {{/if_eq}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>

Try!

Вы можете попробовать пример, нажав на ссылку "Try".

Uncaught Error: if_eq doesn't match if - 3:7

Мне потребовалось некоторое время, чтобы понять в чем же дело, когда я увидел эту ошибку. Возможно, что-то заблокировало мой разум, я не уверен. Сможете ли вы определить проблему в этом примере:

examples/js/handlebars_helpers_if_eq_typo.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if_eq name Foo (expected true)
    {{#if_eq name 'Foo'}}
      true
    {{else}}
      false
    {{/if}}
    
    <br>#if_eq name 'Bar' (expected false)
    {{#if_eq name 'Bar'}}
       true
    {{else}}
       false
    {{/if_eq}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>

Try!

Это случилось, когда я начал преобразовывать оператор if в условие if_eq, но я заменил только открывающий оператор {{#if ...}} на {{#if_eq ...}}, но не заменил закрывающий оператор, который остался таким - {{/if}}/hl>. Таким образом, ошибка говорит нам, что if_eq не соответствует if. Возможно, если бы ключевые слова были как-то выделены, все было бы проще.

В любом случае, будьте осторожны с такими опечатками. Это просто трата времени. Делайте более интересные ошибки!

iff - для других условий

Наконец, сделаем более мощный хелпер для проверки условий. Я назвал его iff. Я знаю математически смысл этого, но название выглядит мило и коротко, чтобы быть использованным в нашем случае. Идея такова, чтобы я мог писать условия вот так:

    {{#iff name '==' 'Foo'}}

и вот так:

    {{#iff answer '>' 40}}

В примере я сделал два шаблона. В первом шаблоне есть три таких условия. Во втором - одно условие {{#iff 4 '*' 5}} - чтобы показать, как будет вести себя хелпер iff, если получит неподдерживаемый оператор. Также я добавил две кнопки - одну для обработки первого шаблона, и вторую - для второго шаблона.

examples/js/handlebars_conditionals.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#iff name '==' 'Foo' (expected true)
    {{#iff name '==' 'Foo'}}
      true
    {{else}}
      false
    {{/iff}}

    <br>#iff 42 &gt; 40 (expected true)
    {{#iff answer '>' 40}}
      true
    {{else}}
      false
    {{/iff}}

    <br>#iff 42 &gt; 50 (expected false)
    {{#iff answer '>' 50}}
      true
    {{else}}
      false
    {{/iff}}
  </script>

  <script id="text2-template" type="text/x-handlebars-template">
    <br>Exception!
    {{#iff 4 '*' 5}}
       true
    {{else}}
       false
    {{/iff}}
  </script>

</head>
<body>
<button id="show">Show</button>
<button id="show2">Try invalid</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_conditionals.js"></script>

</body>
</html>

Try!

В файле JavaScript лежат данные, код, реагирующий на нажатия и обрабатывающий шаблон, и частичная(!) реализация хелпера iff. По сути, это огромный оператор switch, обрабатывающий отдельно в case каждый из допустимых входных операторов. Поведение по умолчанию (default), когда полученный оператор не может быть обработан одним из case, генерирует исключение.

examples/js/handlebars_conditionals.js

var data = {
   'cond1'  : true,
   'cond2'  : false,
   'name'   : 'Foo',
   'answer' : 42
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

document.getElementById('show2').addEventListener('click', function () {
    document.getElementById('content').innerHTML = 'Look at the console!';
    var source = document.getElementById('text2-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});


Handlebars.registerHelper('iff', function(a, operator, b, opts) {
    var bool = false;
    switch(operator) {
       case '==':
           bool = a == b;
           break;
       case '>':
           bool = a > b;
           break;
       case '<':
           bool = a < b;
           break;
       default:
           throw "Unknown operator " + operator;
    }

    if (bool) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});


#compare

Добравшись до этого момента, я узнал, что уже есть реализация подобного хелпера, называемая #compare. Этот хелпер можно найти среди хелперов сравнения.

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