EN RU
Forum

Methodology

Technology

Toolbox

Libraries

Tutorials

What is available in the template body?

While traversing input data, bem-xjst builds a context, which contains:

Normalized information about the current BEM entity

The template engine normalizes data about the current BEM entity. The current BEMJSON node might have incomplete information about the BEM entity. For example:

{
    block: 'page',
    content: {
        elem: 'top'
        // The node doesn’t have the `block` field.
        // But the template engine understands which
        // block context the `top` element is in.
    }
}

Fields with normalized data:

Note that the this.mods and this.elemMods objects always exist, so checking for their presence in the template body is redundant:

block('page').match(function() {
    // Redundant:
    return this.mods && this.mods.type === 'index' && this.ctx.weather;

    // Sufficient:
    return this.mods.type === 'index' && this.ctx.weather;
}).def()(function() { return … });

Current BEMJSON node

The current BEMJSON node is available in the this.ctx field.

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

Result of BEMHTML templating:

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

Helpers

Escape methods

xmlEscape

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

Returns the passed str string with the following XML symbols escaped: &, <, >.

Usage example:

{ block: 'button' }

Template:

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

Result of BEMHTML templating:

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

attrEscape

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

Returns the passed str string with the following characters for XML and HTML attributes escaped: " and &.

jsAttrEscape

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

Returns the passed str string with the following characters escaped: ' and &.

By default, input data from the js field and data from the js mode are escaped using this function.

Position helpers

this.position

The position in the BEM tree (the this.position context field) is a natural number corresponding to the sequential number of the current (contextual) BEM entity in relation to its neighbors in the tree (peer entities).

When calculating the position:

Example of position numbering in the input BEM tree:

{
    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
            ]
        }
    ]
}

The BEM tree may be filled in as templates are executing, by using templates in the def or content mode. This dynamic modification of the BEM tree is taken into account when calculating positions.

The isLast function for determining the last BEM entity among peers returns false if the last element in the array containing the nodes is not a BEM entity.

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

This behavior is explained by the fact that for optimization purposes, BEMHTML does not perform a preliminary traversal of the BEM tree. This means that at the time when the item2 block is processed, the length of the array is already known (item2 is not the last element). However, it is not yet known that the last element is not a BEM element and won’t get a position number.

In practice, this case shouldn’t generate errors, because the check for the first and last BEM entity is normally applied to automatically generated lists, and it doesn’t make sense to include other types of data in them.

isFirst

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

Checks whether the node is the first among its peers in the input tree.

isLast

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

Checks whether the node is the last among its peers in the input tree.

Unique ID generator

this.generateId()

Generates an 'id' for the current node.

Usage example:

// BEMJSON
{ block: 'input', label: 'Name', value: 'John Malkovich' }

Template

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
            }
        }
    ];
});

Result of BEMHTML templating:

<div class="input">
    <label for="uniq14563433829878">Name</label>
    <input id="uniq14563433829878" value="John Malkovich" />
</div>

Other helpers

this.reapply()

This is the ability to template any BEMJSON data located in the template body and get a string as the result.

BEMJSON:

{ block: 'a' }

Template:

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

Result of BEMHTML templating:

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

User-defined custom fields

The context available in the template body can be extended by the user.

Using the oninit function in the template code:

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

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

    // Note: oninit only works for the first template compilation.
    oninit(function(exports, shared) {
        shared.BEMContext.prototype.hi = function(name) {
            return 'Hello, ' + username;
        };
    });

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

var bemjson = { block: 'b' };

// Applying templates
var html = templates.apply(bemjson);

html contains the string:

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

Using the BEMContext prototype:

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

// Extending the context prototype
templates.BEMContext.prototype.hi = function(name) {
    return 'Hello, ' + username;
};

// Adding templates
templates.compile(function() {
    block('b').content()(function() {
        return this.hi('templates');
    });
});

var bemjson = { block: 'b' };

// Applying templates
var html = templates.apply(bemjson));

As a result, html contains the string:

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

Methods for controlling the templating process

The methods apply, applyNext and applyCtx are available in the body of templates. For more information, see the next section on runtime.


Read next: runtime