Шаблонизатор Handlebars для JavaScript предоставляет условный оператор if
(так же возможно использование и else
),
но оператор if
может обрабатывать только единственное значение (не выражение). Вы можете написать
{{#if name}}
..
{{/if}}
но не можете написать
{{#if name == 'Foo'}}
..
{{/if}}
Давайте создадим хелпер (обработчик) для Handlebars, который будет предоставлять такую функциональность.
Условие if
Но перед тем, как создавать хелпер, давайте посмотрим на полноценный пример с использованием оператора if
.
В объекте есть два значения: cond1 и cond2, true и false соответственно. Остальная часть кода это просто получение шаблона
и обработка данных в Handlebars.
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" (откроется в новом окне). Кнопка "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".
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>
Это случилось, когда я начал преобразовывать оператор 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 > 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>
В файле 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
.
Этот хелпер можно найти среди хелперов сравнения.
В любом случае, я думаю, что было интересно узнать, как такое можно сделать и использовать.