idom-template

1.1.3 • Public • Published

idom-template

Now you can speed up and optimize your application, which once used standard templates by incremental DOM. You may read more about it here.

Library for converting your HTML or templates (ejs/underscore templates or like) into incremental-DOM by Google rendering functions.

You can use this library with any framework with you want.

New functionality is available. The documentation going to be updated, but now, see Tests.

Example

The simplest example of use looks like this, html:

<div id="container"></div>
<script type="x-template" id="underscore-template">
    <div> <%- data.listTitle %></div>
    <ul>
        <%  var showFootnote = false;
            data.listItems.forEach(function(listItem, i) {
        %>
        <li class="row <%=(i % 2 == 1 ? ' even' : '')%>">
            <%- listItem.name %>
            <% if (listItem.hasOlympicGold){
            showFootnote = true; %>
            <em>*</em>
            <% } %>
        </li>
        <%  }); %>
    </ul>
 
    <% if (showFootnote){ %>
    <p style="font-size: 12px ;">
        <em>* Olympic gold medalist</em>
    </p>
    <% } %>
</script> 

javascript:

var templateData = {
    listTitle: "Olympic Volleyball Players",
    listItems: [
        {
            name: "Misty May-Treanor",
            hasOlympicGold: true
        },
        {
            name: "Kerri Walsh Jennings",
            hasOlympicGold: true
        },
        {
            name: "Jennifer Kessy",
            hasOlympicGold: false
        },
        {
            name: "April Ross",
            hasOlympicGold: false
        }
    ]
};
 
var templateStr = document.getElementById('underscore-template').innerHTML;
var renderFn = itemplate.compile(templateStr, IncrementalDOM);
 
IncrementalDOM.patch(document.querySelector('#container'), renderFn, templateData);

In this case your template will be compiled by idom-template into the following JS function:

function (data) {
    var o = lib.elementOpen, c = lib.elementClose, t = lib.text, v = lib.elementVoid;
    o('div');
    t(data.listTitle);
    c('div');
    o('ul');
    var showFootnote = false;
    data.listItems.forEach(function (listItem, i) {
        o('li', null, null, 'class', 'row ' + (% 2 == 1 ? ' even' : ''));
        t(listItem.name);
        if (listItem.hasOlympicGold) {
            showFootnote = true;
            o('em');
            t('*');
            c('em');
        }
        c('li');
    });
    c('ul');
    if (showFootnote) {
        o('p', null, null, 'style', 'font-size: 12px ;');
        o('em');
        t('* Olympic gold medalist');
        c('em');
        c('p');
    }
}

Take note that the dependencies: lib(incremental-dom library) и helpers are ejected with closure. The same closure contains the creation of static arrays, when you use them for the array of static attributes.

As opposed to underscore.js, compilation of comments does not take place!

This one and more complicated examples can be viewed in the directory examples.

You may also compare the performance of BackboneJS, BackboneJS + incremental-dom, ReactJS 0.13.3: DEMO

npm

More over then include library directly, You also can use npm for installation:

npm install idom-template --save

Include

    <script src="../lib/incremental-dom.js"></script> 
    <script src="../lib/itemplate.js"></script> 

Use

var templateStr = document.getElementById('underscore-template').innerHTML;
var renderFn = itemplate.compile(templateStr, IncrementalDOM);
 
patch(containerElement, renderFn, templateData);

You should consider the following issues:

  • You should be careful with the ' symbol in templates; if it's mentioned in the text, it should be screened as \'. This will be fixed in further versions.
  • The data is transferred to the template as one object; so if you don't want to transfer data via closure in templates, you should work with one object that will be transferred as a parameter to path.

unwrap

Be careful, if the second parameter is absent during the compilation of the template, which means you won't transmit the link to the library:

var renderFn = itemplate.compile(templateStr[, IncrementalDOM]);

In this case it's not a rendering function that will be compiled, it will be just a function without closure and wrapping:

function (data, lib, helpers){
        // throw error or something if not lib and helpers
        var o=lib.elementOpen,c=lib.elementClose,t=lib.text,v=lib.elementVoid;
        o('div', null, null, 'class', 'box');
            t( data.content );
        c('div');
        helpers['my-console']({data:7+8});
}

In this case you should call it and transmit compilation parameters as follows:

patch(document.getElementById('container'), function () {
    template(data, IncrementalDOM, customHelpers);
});

It allows to work with templates with more flexibility; for example, call several templates in one rendering function, or introduce additional logic, such as filtration etc.

Options

You may set compiling option as object to the library:

itemplate.options({
    //...
});

Where:

  • parameterName - name of the data object, which is transferred to the render function.
  • template (interpolate, escape, evaluate) - regular expression of your templates; you may change them, so that the compiler will process your template syntax.

Take note that compilation is carried out in the following order: interpolate, escape, evaluate. In further versions we plan to provide an opportunity of changing the sequence of template processing.

  • escape, MAP - regular expression and MAP for processing the html escaping expression.

  • accessory (open, close) - service lines for processing interpolate, escape templates; it's better not to modify them.

  • staticKey - attribute name for static attributes array generation in current tag. See static attributes.

By default the options have the following values:

 {
    BREAK_LINE: /(\r\n|\n|\r)\s{0,}/gm,
    // prepare options
    template: {
        evaluate: /<%([\s\S]+?)%>/g,
        interpolate: /<%=([\s\S]+?)%>/g,
        escape: /<%-([\s\S]+?)%>/g
    },
    order: ['interpolate', 'escape', 'evaluate'],
    evaluate: {
        name: 'script',
        open: '<script>',
        close: '</script>'
    },
    accessory: {
        open: '{%',
        close: '%}'
    },
    escape: /(&amp;|&lt;|&gt;|&quot;)/g,
    MAP: {
        '&amp;': '&',
        '&lt;': '<',
        '&gt;': '>',
        '&quot;': '"'
    },
    // build options
    emptyString: true,
    staticKey: 'key',
    staticArray: 'static-array',
    nonStaticAttributes: ['id', 'name'],
    parameterName: 'data',
    parentParameterName: 'parent',
    renderContentFnName: 'content',
    // tags parse rules
    textSaveTags: ['pre', 'code'],
    voidRequireTags: ['input', 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'keygen', 'link', 'meta',
        'param', 'source', 'track', 'wbr'],
    debug: false
}

You may modify any option.

Static attributes

Arrays of static attributes are used to save memory.

For generation of a static array, you should select the static-array attribute from element attriibutes and add it to the template tag.

The value of this attribute will become the name of the static array:

  • in case the value is not specified, the array will be generated, and its name will be a unique generated line.
  • in case different tags contain the same name of the static array, the same array will be used for all of these elements. This generated array will be based on all static attributes of the last tag with this key in the template.
  • the name of the attribute can be changed in options.

Example

If you compile the function of the following template:

    <div class="box" key="box_class" data-key="my-custom-key"
         style="top: <%= data.top %>px; left: <%= data.left %>px; background: rgb(0,0,<%= data.color %>);">
        <%= data.content %>
    </div>

You will get the result:

(function (lib, helpers) {
    var box_class = ['class', 'box', 'data-key', 'my-custom-key'];
    return function (data) {
        var o = lib.elementOpen, c = lib.elementClose, t = lib.text, v = lib.elementVoid;
        o('div', 'ZYjoAthjdzUz', box_class, 'style', 'top: ' + data.top + 'px; left: ' + data.left + 'px; background: rgb(0,0,' + data.color + ');');
        t(data.content);
        c('div');
    }
})(IncrementalDOM, {
    // ... helpers object
});

It's important to understand that arrays of static attributes are unqiue for every template. If you use the same key name in different templates, there will be different arrays with different values.

Take note that:

  • if you use an array of static attributes, a key for this element will be generated automatically.

Helpers

There is an option of injecting JS functions as a part of the compiled template.

For that purpose we use self-closing tag (with /). All attributes of this tag will be moved to the JS function as a data object with keys, which are attributes of the tag.

That is, upon registering the following helper:

itemplate.registerHelper('my-console', function (attrs) {
    console.log(attrs);
});

...where the first parameter is the helper name, and the second one is the JS function.

In this case, in order to call the helper it will suffice to indicate the tag is your template:

<!-- ... -->
<my-console id="console_1" data="<%=7+8%>"/>
<!-- ... -->

Every time the template is rendered, the registered function will be executed in the place where the given tag is inserted. This is what will be in the console:

{id: 'console_1', data: 15}

This option can be used in the following ways:

Insertion of templates:

You may register any rendering function of incremental DOM as a helper. In this case external templates will be inserted into the rendering function.

For example, having registered the following template as a helper:

 var footnoteRenderFn = itemplate.compile(footnoteTemplate, IncrementalDOM);
 itemplate.registerHelper('my-footnote', footnoteRenderFn);

You will be able to simply insert it in another template in the following way .ejs:

<my-footnote listItems="<%=data.listItems%>"/>

Auxiliary logic of templates:

It's similarly possible to describe synchronous auxiliary logic using helpers.

var itemRenderFn = itemplate.compile(itemTemplate, IncrementalDOM);
itemplate.registerHelper('my-list', function (attrs) {
    elementOpen('ul');
    _.each(attrs.listItems, function (listItem, i) {
        // render single list item
        itemRenderFn({
            i: i,
            name: listItem.name,
            hasOlympicGold: listItem.hasOlympicGold
        });
    });
    elementClose('ul');
});

The following line in your template will be responsible for full rendering of the .ejs list:

<my-list listItems="<%=data.listItems%>"/>

Examples of use of helpers can be viewed in the directory examples

Please consider:

  • data in helpers can be transmitted only via tag attributes. For example, the internal template will have access only to the data received via attributes.
  • helpers are not web-components, so they work only if you have registered helper beforehand, and then compiled the template that uses it.

Package Sidebar

Install

npm i idom-template

Weekly Downloads

16

Version

1.1.3

License

MIT

Last publish

Collaborators

  • mobidev
  • r37r0m0d3l
  • yluchaninov