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>