JavaScript
Handlebars

Search for '{{search_term}}'

Условия в Handlebars

CMOS is the Code-Maven Open Source podcast that also includes video interviews. Subscribe to this feed RSS feed with your Podcast listener app or via iTunes iTunes.

Шаблонизатор 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. Этот хелпер можно найти среди хелперов сравнения.

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

Savenkova Natalya
Переводчик
Savenkova Natalya
Gabor Szabo
Автор
Gabor Szabo

Comments