RU
Форум

Методология

Инструментарий

Платформа

Что доступно в теле шаблона?

В процессе обхода входных данных bem-xjst строит контекст, который будет содержать:

Нормализованные сведения о текущей БЭМ-сущности

Шаблонизатор нормализует сведения о текущей БЭМ-сущности. В текущем BEMJSON-узле могут быть неполные сведения о БЭМ-сущности:

{
    block: 'page',
    content: {
        elem: 'top'
        // У узла нет поля `block`.
        // Но шаблонизатор поймёт, в контексте какого блока находится элемент `top`.
    }
}

Поля с нормализованными данными:

Обратите внимание, что объекты this.mods и this.elemMods всегда присутствуют, поэтому проверки на их наличие в теле шаблона избыточные:

block('page').match(function() {
    // Избыточно:
    return this.mods && this.mods.type === 'index' && this.ctx.weather;

    // Достаточно:
    return this.mods.type === 'index' && this.ctx.weather;
}).def()(function() { return … });

Текущий узел BEMJSON

В поле this.ctx доступен текущий узел BEMJSON.

{
    block: 'company',
    name: 'yandex'
}
block('company').attr()(function() {
    return {
        id: this.ctx.name,
        name: this.ctx.name
    };
});

Результат шаблонизации:

<div class="company" id="yandex" name="yandex"></div>

Хелперы

Методы для экранирования

xmlEscape

/**
 * @param {String} str
 * @returns {String}
 */
this.xmlEscape(str)

Возвращает переданную строку str с заэкранированными символами XML: &, <, >. Ожидается, что str это строка. Но если str это undefined, Null или NaN, то функция возвратит пустую строку. Если str любого другого типа, то перед экранированием этот тип будет приведен к строке.

Пример использования:

{ block: 'button' }

Шаблон:

block('button').def()(function() {
    return this.xmlEscape('<b>&</b>');
});

Результат шаблонизации:

&lt;b&gt;&amp;&lt;/b&gt;

attrEscape

/**
 * @param {String} str
 * @returns {String}
 */
this.attrEscape(str)

Возвращает переданную строку str с заэкранированными символами XML- и HTML-атрибутов: " и &. Ожидается, что str это строка. Но если str это undefined, Null или NaN, то функция возвратит пустую строку. Если str любого другого типа, то перед экранированием этот тип будет приведен к строке.

jsAttrEscape

/**
 * @param {String} str
 * @returns {String}
 */
this.jsAttrEscape(str)

Возвращает переданную строку str с заэкранированными символами: ' и &. Ожидается, что str это строка. Но если str это undefined, Null или NaN, то функция возвратит пустую строку. Если str любого другого типа, то перед экранированием этот тип будет приведен к строке.

По умолчанию входные данные из поля js и данные из режима js экранируются этой функцией.

Хелперы определения позиции

this.position

Позиция в БЭМ-дереве (поле контекста this.position) представляет собой натуральное число, соответствующее порядковому номеру текущей (контекстной) БЭМ-сущности среди её соседей в дереве (одноранговых сущностей).

При вычислении позиции:

Пример нумерации позиций во входном БЭМ-дереве:

{
    block: 'page',                // this.position === 1
    content: [
        { block: 'head' },        // this.position === 1
        'text',                   // this.position === undefined
        {
            block: 'menu',        // this.position === 2
            content: [
                { elem: 'item' }, // this.position === 1
                'text',           // this.position === undefined
                { elem: 'item' }, // this.position === 2
                { elem: 'item' }  // this.position === 3
            ]
        }
    ]
}

БЭМ-дерево может быть достроено в процессе выполнения шаблонов с помощью шаблонов в режиме def или content. Такое динамическое изменение БЭМ-дерева учитывается при вычислении позиции.

Функция определения последней БЭМ-сущности среди соседей isLast возвратит false, если в массиве, содержащем узлы, последний элемент не является БЭМ-сущностью.

block('list')(
    content()([
        { block: 'item1' },
        { block: 'item2' }, // this.isLast() === false
        'text'
    ])
);

Такое поведение объясняется тем, что в целях оптимизации BEMHTML не выполняет предварительного обхода БЭМ-дерева. Поэтому в момент обработки блока item2 уже известна длина массива (item2 не является последним элементом), но еще неизвестно, что последний элемент не является БЭМ-сущностью и не получит номера позиции.

На практике описанный случай не должен порождать ошибок, так как проверка на первую/последнюю БЭМ-сущность обычно применяется к автоматически сгенерированным спискам, в которые не имеет смысла включать данные других типов.

isFirst

/**
 * @returns {Boolean}
 */
this.isFirst()

Проверяет, является ли узел первым среди своих соседей во входном дереве.

isLast

/**
 * @returns {Boolean}
 */
this.isLast()

Проверяет, является ли узел последним среди своих соседей во входном дереве.

Генератор уникальных идентификаторов

this.generateId()

Генерирует id для текущего узла.

Пример использования:

// BEMJSON
{ block: 'input', label: 'Имя', value: 'Иван' }

Шаблон:

block('input').content()(function() {
    var id = this.generateId();

    return [
        {
            tag: 'label',
            attrs: { for: id },
            content: this.ctx.label
        },
        {
            tag: 'input',
            attrs: {
                id: id,
                value: this.ctx.value
            }
        }
    ];
});

Результат шаблонизации:

<div class="input">
    <label for="uniq14563433829878">Имя</label>
    <input id="uniq14563433829878" value="Иван" />
</div>

Другие хелперы

this.reapply()

Это возможность отшаблонизировать произвольные BEMJSON-данные, находясь в теле шаблона и получить в результате строку.

BEMJSON:

{ block: 'a' }

Шаблон:

block('a').js()(function() {
    return {
        template: this.reapply({ block: 'b', mods: { m: 'v' } })
    };
});

Результат шаблонизации:

<div class="a i-bem" data-bem='{
    "a":{"template":"<div class=\"b b_m_v\"></div>"}}'></div>

Кастомные пользовательские поля

Контекст, доступный в теле шаблона, может быть расширен пользователем.

При помощи функции oninit в коде шаблонов:

var bemxjst = require('bem-xjst');

var templates = bemxjst.bemhtml.compile(function() {

    // Внимание: oninit сработает только при первой компиляции шаблонов.
    oninit(function(exports, shared) {
        shared.BEMContext.prototype.hi = function(username) {
            return 'Hello, ' + username;
        };
    });

    block('b').content()(function() {
        return this.hi('username');
    });
});

var bemjson = { block: 'b' };

// Применяем шаблоны
var html = templates.apply(bemjson);

html будет содержать строку:

<div class="b">Hello, username</div>

При помощи прототипа BEMContext:

var bemxjst = require('bem-xjst');
var templates = bemxjst.bemhtml.compile('');

// Расширяем прототип контекста
templates.BEMContext.prototype.hi = function(name) {
    return 'Hello, ' + username;
};

// Добавляем шаблоны
templates.compile(function() {
    block('b').content()(function() {
        return this.hi('templates');
    });
});

var bemjson = { block: 'b' };

// Применяем шаблоны
var html = templates.apply(bemjson));

В результате html будет содержать строку:

<div class="b">Hello, templates</div>

Методы для управления шаблонизацией

В теле шаблонов доступны методы apply, applyNext и applyCtx. Подробнее о них читайте в следующей секции про runtime.

Функции шаблонов

В случае, когда тело шаблона является функцией, она вызывается с двумя аргументами:

Пример

block('link').attrs()(function(node, ctx) {
    return {
        // тоже самое что и this.ctx.url
        href: ctx.url,

        // тоже самое что и this.position
        'data-position': node.position
    };
});

Такие же аргументы доступны в функции подпредикате match().

match(function(node, ctx) {
    // тоже самое что и this.mods.disabled
    return !node.mods.disabled &&
        // тоже самое что и this.ctx.target
        ctx.target;
})

Кроме того, функции шаблонов поддерживают ES6 arrow functions, поэтому вы можете писать везде в таком стиле:

match((node, ctx) => (!node.mods.disabled && ctx.target))
addAttrs()((node, ctx) => ({ href: ctx.url }))

Читать далее: runtime

Оцените статью
Сообщить об ошибке на Гитхабе или исправить в prose.io.