Синтаксис шаблонов
Синтаксис шаблонов Wasaby является XHTML-совместимым. Используя шаблоны, вы можете в декларативной форме связывать данные контрола с элементами DOM. Для шаблонов мы используем собственное расширение файла — WML (Wasaby Markup Language). Необходимым условием валидности шаблона является наличие единственного корневого элемента.
<!-- WML -->
<div>
<!-- контент шаблона -->
</div>
Конструкция шаблонизатора
Шаблонизатор Wasaby распознаёт конструкции, созданные с помощью синтаксиса Mustache (двойных фигурных скобок). Выражение этих конструкций можно составлять из переменных, находящихся в области видимости шаблона, а также из синтаксических конструкций языка JavaScript.
<!-- WML -->
<div>
{{ выражение }}
</div>
При обработке шаблона вычисляется выражение
конструкции, его значение экранируется и, как следствие, выводится на странице как простой текст (не HTML). Таким образом обеспечивается защита от XSS-уязвимостей.
Существуют и другие конструкции, которые будут разобраны далее в рамках руководства.
Вывод текста
По умолчанию значение конструкций выводится на страницу как текст. В следующем примере показано как вывести в шаблон значение переменной text
.
<!-- WML -->
<div title="{{ text }}">
{{ text }}
</div>
Вывод JsonML
Значение конструкции можно вывести как HTML. Для этого необходимо использовать Controls/decorator:Markup. Контрол принимает данные формата JsonML, а перед вставкой выполняет санитизацию небезопасных тегов.
<!-- WML -->
<div>
<Controls.decorator:Markup value="{{ json }}" />
</div>
// JavaScript
var ModuleClass = Base.Control.extend({
_template: template,
json: '[["p", {"version":"2.0"}, ["a", {href: "https://saby.ru"}, "https://saby.ru"]]]'
});
Для отображения спецсимволов html рекомендуется либо вставлять в шаблон сам символ, либо его числовой код. В следующем примере в обоих случаях в вёрстку страницы выводится символ рубля:
<!-- WML -->
<div>
₽
₽
</div>
Результат:
<div>
₽
₽
</div>
Использование JavaScript
Конструкции шаблонизатора поддерживают работу с выражениями языка JavaScript: арифметические операции, операторы сравнения и условные операторы, использование переменных из области видимости шаблона.
<!-- WML -->
<div>
<div>{{true}}</div>
<div>{{ 25 }}</div>
<div>{{ 24 + 8 }}</div>
<div>{{ 'hello' }}</div>
<div>{{ "Hello" + ", " + "Wasaby!" }}</div>
<div>{{ ({'a': 5, 'b': 'blue'}).a }}</div>
<div>{{ ['days','color'][1] }}</div>
<div>{{ number + 1 }}</div>
<div>{{ bar && number }}</div>
<div>{{ bar ? "hidden" : "visible" }}</div>
<div>my-class {{ bar ? number }}</div>
<div>{{ number > 20 }}</div>
</div>
См. демо-пример на CodeSandbox
В примере выше использован тернарный оператор:
<!-- WML -->
<div>my-class {{ bar ? number }}</div>
В JavaScript
такой синтаксис оператора считается некорректным, правильно использовать выражение вида test ? consequent : alternate.Однако в шаблонах
Wasaby Framework
тернарный оператор может использовать без alternate-части, тогда в качестве alternate
будет использоваться:- пустая строка, если оператор используется в тексте, атрибуте тега, атрибуте компонента с префиксом
attr;
или атрибуте/опции с именамиclass
иstyle
. - undefined, для всех остальных случаев (опций компонента).
<!-- WML -->
<UIModule.Component
<!-- В качестве alternate-части использется undefined -->
option="{{ hasOption ? someOption }}"
<!-- В качестве alternate-части используется пустая строка -->
attr:class="{{ hasClass ? 'class-for-component' }}">
<ws:content>
<!-- В качестве alternate-части используется пустая строка -->
<div class="pre--{{ divClass ? 'class-for-div' }}--post">{{ hasText ? someText }}</div>
</ws:content>
</UIModule.Component>
В рамках конструкции шаблонизатора допустимо использовать только одно выражение языка JavaScript. Запрещено объявлять новые переменные, использовать нативные функции языка и т.п. Ниже приведены конструкции, которые являются синтаксической ошибкой:
<!-- WML -->
<div>
{{ var name = 'Wasaby'; "Hello" + name }}
{{ console.log('debug') }}
{{ if (bar) number }}
</div>
См. демо-пример на CodeSandbox
Условия
Директива ws:if
Директива <ws:if>
позволяет добавлять html-разметку при выполнении условия, описанного в атрибуте data
. Значением атрибута является конструкция шаблонизатора. Когда атрибут принимает значение true
, в DOM вставляется содержимое блока <ws:if>
.
<!-- WML -->
<div>
<ws:if data="{{ someOpt === 'saby' }}">
<a href="https://saby.ru/">saby.ru</a>
</ws:if>
</div>
См. демо-пример на CodeSandbox
Не рекомендуется к использованию
Директива имеет короткую форму записи — атрибут if
, который можно назначить любому html-тегу в шаблоне. Смысл атрибута идентичен атрибуту data
. Когда атрибут принимает значение true
, в DOM вставляется и содержимое тега, и сам тег.
<!-- WML -->
<div>
<a href="https://saby.ru/" if="{{ someOpt === 'url' }}">saby.ru</a>
</div>
Директива ws:else
Директиву <ws:if>
можно дополнить блоками <ws:else>
. Для всех блоков, кроме последнего, допускается создание условия в атрибуте data
.
<!-- WML -->
<ws:if data="{{ someOpt === 'saby.ru' }}">
<a href="https://saby.ru/">saby.ru</a>
</ws:if>
<ws:else data="{{ someOpt === 'wi' }}">
<a href="https://wi.sbis.ru/">wi.sbis.ru</a>
</ws:else>
<ws:else>
<a href="https://online.sbis.ru.ru/">online.sbis.ru</a>
</ws:else>
Между блоками <ws:if>
и <ws:else>
запрещается встраивание разметки. Это приводит к синтаксической ошибке.
Область видимости директивы
Директивы <ws:if>
и <ws:else>
не создают новую область видимости переменных (см. Подробнее о шаблонах), поэтому переменные родительского шаблона доступны к использованию в любом из блоков директивы.
Циклы
Директива ws:for
С помощью директивы <ws:for>
можно реализовать добавление фрагментов разметки в цикле. Для описания выражения цикла используют атрибут data
, а тело цикла описывают в содержимом блока <ws:for>
.
В атрибуте data
допускается организовывать итерации по целочисленному диапазону, объектам, массивам и экземплярам классов Types/entity:Model или Types/entity:Record. Для каждого типа данных используется собственная форма записи.
Итерация по целочисленному диапазону или элементам массива:
<!-- WML -->
<div>
<ws:for data="i in number">
{{i}};
</ws:for>
</div>
См. демо-пример на CodeSandbox
Итерация по свойствам объекта или элементам массива:
<!-- WML -->
<div>
<ul>
<ws:for data="key, value in food">
<li>{{ key }} is {{ value }};</li>
</ws:for>
</ul>
</div>
См. демо-пример на CodeSandbox
Не рекомендуется к использованию
Директива <ws:for>
имеет короткую форму записи — атрибут for
, который можно назначить любому html-тегу в шаблоне. Смысл атрибута идентичен атрибуту data
.
<div>
<ul>
<li for="goods in food">{{ goods.name }} is {{ goods.color }};</li>
</ul>
</div>
См. демо-пример на CodeSandboxОбласть видимости директивы
Директива создаёт новую область видимости переменных (см. Подробнее о шаблонах). Переменные видны только внутри оператора, но не за его пределами.
Встраивание контрола или внешнего шаблона
По имени
Для добавления в шаблон контрола можно применять специальный синтаксис, где имя контрола — это имя тега. Если в имени встречается символ "/", тогда при добавлении в шаблон его заменяют на символ точки.
Встроенный таким образом контрол будет автоматически загружен Wasaby, поэтому нет необходимости указывать его в массиве зависимостей модуля.
<!-- WML -->
<div>
<!-- Встраивание контрола -->
<Spoiler.Module />
<!-- Встраивание контрола из JS библиотеки-->
<Demo.foo:Module />
</div>
См. демо-пример на CodeSandbox
Директива ws:partial
Для добавления в шаблон контрола или другого шаблона можно применять директиву <ws:partial>
. Директиве в атрибут template
передают имя контрола или шаблона (с указанием плагина wml!
).
<!-- WML -->
<div>
<!-- Встраивание шаблона -->
<ws:partial template="wml!Demo/OtherTemplate"/>
<!-- Встраивание контрол -->
<ws:partial template="Spoiler/Module"/>
</div>
Встроенные таким образом контрол или шаблон будут автоматически загружены Wasaby, поэтому нет необходимости указывать их в массиве зависимостей модуля.
Значение атрибута можно вычислять динамически с помощью синтаксических конструкций. Однако в этом случае не происходит автоматической загрузки зависимости.
<!-- WML -->
<div>
<!-- Значение атрибута вычисляется динамически -->
<ws:partial template="{{ name }}"/>
</div>
Передача опций
Добавленный контрол/шаблон создаёт изолированную область видимости, в которой недоступны переменные родительского шаблона. Однако эти переменные можно передать в качестве опций, и делают это с помощью атрибутов.
В рамках контрола или шаблона работа с опциями подчиняется этим правилам.
В следующем примере показано, что в области видимости родительского шаблона существует переменная caption
и name
. С помощью атрибутов newCaption
и newName
мы создали опции и присвоили им значения.
<!-- WML -->
<div>
<Spoiler.Module newCaption="{{ caption }}" />
<ws:partial template="wml!Demo/OtherTemplate" newName="{{ name }}" />
</div>
См. демо-пример на CodeSandbox
Шаблонизатор поддерживает синтаксис XML для описания передаваемых опций. Для этого следует использовать следующую форму:
<ws:optionName>
<ws:optionType>optionValue</ws:optionType>
</ws:optionName>
<!-- WML -->
<div>
<Spoiler.Module>
<ws:myOpt1>
<ws:Number>20</ws:Number>
</ws:myOpt1>
<ws:myOpt2>
<ws:String>New message</ws:String>
</ws:myOpt2>
<ws:myOpt3>
<ws:Boolean>true</ws:Boolean>
</ws:myOpt3>
<ws:myOpt4>
<ws:Object>
<ws:key>
<ws:Number>1</ws:Number>
</ws:key>
<ws:title>
<ws:String>Пункт меню</ws:String>
</ws:title>
<ws:folder>
<ws:Boolean>true</ws:Boolean>
</ws:folder>
<ws:style>
<ws:Object>
<ws:title>
<ws:String>Пункт меню</ws:String>
</ws:title>
<ws:key>
<ws:Number>1</ws:Number>
</ws:key>
</ws:Object>
</ws:style>
<ws:countries>
<ws:Array>
<ws:String>Russia</ws:String>
<ws:String>Ukraine</ws:String>
<ws:String>Belarus</ws:String>
</ws:Array>
</ws:countries>
</ws:Object>
</ws:myOpt4>
<ws:myOpt5>
<ws:Array>
<ws:Number>1</ws:Number>
<ws:Object key="1" title="Russia" />
<ws:String>Пункт меню</ws:String>
<ws:Boolean>true</ws:Boolean>
</ws:Array>
</ws:myOpt5>
<ws:myOpt6>
<ws:Function arg1="{{value1}}" arg2="{{value2}}">MyModule/MyControl:prototype.myFunction</ws:Function>
</ws:myOpt6>
</Spoiler.Module>
</div>
Полный список вариантов конфигурации опций см. в демо-примере на CodeSandbox.
Передача атрибутов
Для встроенного контрола/шаблона используется другой синтаксис для обозначения атрибутов — префикс attr
. Описанные таким образом атрибуты будут добавлены на корневой DOM-элемент.
В следующем примере показано, что на корневой DOM-элемент будет добавлен атрибут class
со значением myClass
.
<!-- WML -->
<div>
<Spoiler.Module attr:class="myClass"/>
</div>
/* CSS */
.myClass {
color: red;
}
См. демо-пример на CodeSandbox
Встроенный шаблон
С помощью директивы <ws:template>
вы можете объявить встроенный шаблон — фрагмент вёрстки, предназначенный для локального использования в рамках родительского шаблона. Встроенный шаблон объявляют до момента его использования, обычно в начале файла. В атрибуте name
задают имя.
В области видимости встроенного шаблона доступны все переменные из области видимости родительского шаблона.
Для добавления встроенного шаблона используют директиву <ws:partial>
.
<!-- WML -->
<div>
<ws:template name="myTemplate">
<p>Список разделов</p>
<ul>
<ws:for data="key, value in links">
<li><a href="{{value}}">{{key}}</a></li>
</ws:for>
</ul>
</ws:template>
...
<ws:partial template="myTemplate" />
</div>
См. демо-пример на CodeSandbox
Обработчик события внутри встроенного шаблона и добавление шаблона через цикл ws:for
В настоящий момент при использовании директивы ws:for есть ограничение. Не поддерживает вывод встроенного шаблона, где используется обработчик события.