Templates

The core template mechanic in Symbiote.js - is a native browser HTML-string parsing via standard DOM API methods. That's the fastest way to create component template instance in object model representation.

html

We have a html helper tag function, that manage to construct templates using compact binding-maps:

import { html } from '@in-wave/symbiote';

const myTemplate = html`
  <button ${{onclick: 'onBtnClick'}}>Click me!</button>
`;

In this example, we've created the button and connected it to a onBtnClick handler function, which should be defined in a component state.

As you can see, every binding-map - is a simple JavaScript object, that describes connections between the own inner element properties and the component's data. It is a standard JavaScript template literal syntax with no any special additions.

Note, that in Symbiote.js you can define and describe template outside the component's context (this) visibility, this is a clear abstraction. That helps to make manipulations with templates much more flexible than in some other libraries.

Binding to a text node

Let's see onto the more detailed example:

import Symbiote, { html } from '@in-wave/symbiote';

class MyComponent extends Symbiote {

  init$ = {
    name: 'John',
    btnTxt: 'Click me!',
    onBtnClick: () => {
      console.log('Button clicked!');
    },
  }

}

MyComponent.template = html`
  <h1>Hello {{name}}!</h2>
  <button ${{onclick: 'onBtnClick'}}>{{btnTxt}}</button>
`;

In this example we have a new type of template bindings to a text nodes, which are defined with a double braces syntax - {{myProp}}.

Binding to nested properties

Symbiote.js allows to bind properties to the nested properties of the elements:

MyComponent.template = html`
  <div ${{'style.color': 'myCssValue'}}>Some text...</div>
`;

Also, you can bind your props to the nested component's state directly, using $-proxy:

MyComponent.template = html`
  <my-component ${{'$.nestedPropName': 'propName'}}></my-component>
`;

Binding to HTML-attributes

To bind some property to the element's attribute, use @ prefix:

MyComponent.template = html`
  <div ${{'@hidden': 'isHidden'}}></div>
`;

Type casting

You can transform any type of property value to boolean type (true or false), using ! symbol.

Inversion example:

html`<div ${{'@hidden': '!innerText'}}> ... </div>`;

Double inversion example:

html`<div ${{'@contenteditable': '!!innerText'}}> ... </div>`;

Property Tokens (Property key prefixes)

Named context property /

To bind something to some named data context property, use the named prefix:

html`<div ${{textContent: 'MY_APP/propName'}}> ... </div>`;

Shared context property *

To share a property with other components in a same usage context, use the shared context token:

html`<div ${{textContent: '*propName'}}> ... </div>`;

Inherited context property ^

To get the direct access to some property of the top level component (cascade data model), use the inheritance token:

html`<div ${{textContent: '^propName'}}> ... </div>`;

CSS Data property --

To initiate some property from the CSS Data, you can do as follows:

html`<div ${{textContent: '--prop-name'}}> ... </div>`;

More details about Symbiote-component's context you can find in Context section.

Slots

Slots allow you to define placeholders in your template that can be filled with any external markup fragment.

Default template slot:

<div class="my-wrapper">
  <slot></slot>
</div>

Named template slots:

<header>
  <slot name="header"></slot>
</header>
<article>
  <slot name="article"></slot>
</article>
<footer>
  <slot name="footer"></slot>
</footer>

Element references

If you need an element reference somewhere in your code logics, use ref attribute for your template element:

html`
  <div>
    <div ${{ref: 'div1'}}></div>
    <div ${{ref: 'div2'}}></div>
  </div>
`;

or, to get the same effect:

html`
  <div>
    <div ref="div1"></div>
    <div ref="div2"></div>
  </div>
`;

Reference name should be unique for each element (like an element's id). Then you can use ref collection to get those elements in your code without any additional DOM search:

class MyComponent extends Symbiote {
  renderCallback() {
    this.ref.div1.contenteditable = true;
    this.ref.div2.style.color = 'red';
  }
}

Dynamic list rendering

To render efficient dynamic reactive list of elements, use the itemize API:

class MyComponent extends Symbiote {
  init$ = {
    listData: [
      {
        firstName: 'John',
        secondName: 'Snow',
      },
      {
        firstName: 'Jane',
        secondName: 'Stone',
      },
    ],
  }
}

MyComponent.template = html`
  <h1>My list:</h1>
  <ul ${{itemize: 'listData'}}>
    <li>{{firstName}} {{secondName}}<li>
  </ul>
`;

More information about itemize API you can find at the List items section.

External customizable templates

Symbiote.js allows you to create components, that can connect templates defined in common HTML-document.

To use this feature, set the allowCustomTemplate flag:

class MyComponent extends Symbiote {

  allowCustomTemplate = true;

}

Then, define necessary templates in the HTML markup:

<template id="first">
  <h1>{{headingText}}</h1>
</template>

<template id="second">
  <h2>{{headingText}}!</h2>
</template>

Ok, now we can use them:

<my-component use-template="#first"><my-component>

<my-component use-template="#second"><my-component>