Условия в Handlebars
Шаблонизатор 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 4 '*' 5}} - чтобы показать, как будет вести себя
хелпер iff, если получит неподдерживаемый оператор. Также я добавил две кнопки - одну для
обработки первого шаблона, и вторую - для второго шаблона.
examples/js/handlebars_conditionals.html
В файле JavaScript лежат данные, код, реагирующий на нажатия и обрабатывающий шаблон, и частичная(!) реализация хелпера iff.
По сути, это огромный оператор switch, обрабатывающий отдельно в case каждый из допустимых входных операторов.
Поведение по умолчанию (default), когда полученный оператор не может быть обработан одним из case,
генерирует исключение.
examples/js/handlebars_conditionals.js
Добравшись до этого момента, я узнал, что уже есть реализация подобного хелпера, называемая #compare.
Этот хелпер можно найти среди хелперов сравнения.
В любом случае, я думаю, что было интересно узнать, как такое можно сделать и использовать.
iff - для других условий
{{#iff name '==' 'Foo'}}
{{#iff answer '>' 40}}
<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 > 40 (expected true)
{{#iff answer '>' 40}}
true
{{else}}
false
{{/iff}}
<br>#iff 42 > 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!
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
Published on 2015-05-11