dna

Evolution-based components.

Rendering

Templates are a vital feature for components, but they are complex too. Moreover, in order to have good performance and a correct behavior, DNA components should implements a Virtual DOM strategy. Both Custom Elements implementations and the IDOM one use IncrementalDOM for this scope.

Using the IncrementalDOM helper

An IncrementalDOM patch function can be defined as a template property for the Component.

import { BaseComponent, IDOM, define } from '@dnajs/idom';

class MyComponent extends BaseComponent {
    get properties() {
        return {
            firstName: String,
            lastName: String,
        };
    }
    get template() {
        return () => {
            IDOM.h('div', { 'class': 'card-wrapper' }, [
                 IDOM.h('span', { 'class': 'card-name' }, this.firstName),
                 IDOM.h('span', { 'class': 'card-last-name' }, this.lastName),
            ]);
        };
    }
}

define('my-component', MyComponent);

This will render as:

<my-component>
    <div class="card-wrapper">
        <span class="card-name">Alan</span>
        <span class="card-last-name">Turing</span>
    </div>
</my-component>

Using JSX

Write Virtual DOM templates can be a boring and a repetitive work. JSX (with a transpiler) can save a lot of time to the developer with the benefit of a clearer syntax.

import { BaseComponent, IDOM, define } from '@dnajs/idom';

const template = function() {
    return <div class="card-wrapper">
        <span class="card-name">{this.firstName}</span>
        <span class="card-last-name">{this.lastName}</span>
    </div>;
}

class MyComponent extends BaseComponent {
    get properties() {
        return {
            firstName: String,
            lastName: String,
        };
    }
    get template() {
        return template;
    }
}

define('my-component', MyComponent);

Take a look here for more JSX templates resources.

Template as strings

For simple components, a Virtual DOM strategy can be ignored using string templates. DNA does not include any template engine (btw, ES2015 String Literals can be a good native choice) but provides a rendering pattern through the TemplateMixin: a template getter defines the component template, while the render method sets the strategy for rendering in a DOM tree.

import { BaseComponent, DOM, define } from '@dnajs/core';

class MyComponent extends BaseComponent {
    get properties() {
        return {
            firstName: String,
            lastName: String,
        };
    }
    get template() {
        return `<div class="card-wrapper">
                    <span class="card-name">${this.firstName}</span>
                    <span class="card-last-name">${this.lastName}</span>
               </div>`;
    }
}

define('my-component', MyComponent);

// ---

let elem = new MyComponent();
elem.firstName = 'Alan';
elem.lastName = 'Turing';
DOM.appendChild(document.body, elem);

By default, TemplateMixin observes for state changes with the PropertiesMixin's propertyChangedCallback to trigger re-rendering.

The render method handles different template return values:

Template engines

To extend the render method can add support for different template engines. For example, in the dnajs/idom package the template function is passed to the IncrementalDOM patch method.

This is a lite component with Handlebars implementation as template engine:

import { BaseComponent } from '@dnajs/core';

class HandlebarsComponent extends BaseComponent {
    render() {
        // of course, it should be better to compile the template once in the getter, but it is just a demo
        let template = Handlebars.compile(this.template);
        super.render(template.bind(this, this));
    }
}

class MyComponent extends HandlebarsComponent {
    get properties() {
        return {
            firstName: String,
            lastName: String,
        };
    }
    get template() {
        return `<div class="card-wrapper">
                    <span class="card-name">{{firstName}}</span>
                    <span class="card-last-name">{{lastName}}</span>
               </div>`
    }
}