modulor-html
provides a way to efficiently (re)render templates to DOM.
It is highly influenced by lit-html
and designed to be compatible with it.
The main exports are:
-
html
: creates template function (wire) -
render
: renders template into DOM container
Basic example (demo)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = (myVar) => html`
<span>Hello ${myVar}</span>
`;
render(tpl('world'), container);
//or alternative way
tpl('world')(container);
Update (demo)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = (date) => html`
<span>Time: ${date.toLocaleTimeString()}</span>
`;
setInterval(() => {
render(tpl(new Date()), container);
}, 1000);
Arrays (demo)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = (names) => html`
<span>Hello:</span>
<ul>
${names.map((name) => html`
<li>${name}</li>
`)}
</ul>
`;
render(tpl(['Steven', 'John']), container);
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = (myVar) => html`
<span>Hello ${myVar}</span>
`;
render(tpl(Promise.resolve('world')), container);
HTML elements (demo)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = ($el) => html`
<div class="wrapper">${$el}</div>
`;
const $element = document.createElement('span');
$element.classList.add('my-class');
$element.innerText = 'i am element';
render(tpl($element), container);
Functions (demo)
Values as functions can be used to get low-level access to rendering process. In most cases you won't need it.
Such functions are called with only one argument container
, which has standard Node
api such as .appendChild()
and so on. (execution of html
itself returns such function)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = (fn) => html`
<span>counter: ${fn}</span>
`;
const fn = (container) => {
let i = 0;
const $text = document.createTextNode(i);
container.appendChild($text);
setInterval(() => {
$text.textContent = i++;
}, 1000);
}
render(tpl(fn), container);
Basic example (demo)
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = ({ checked, dynamicAttr }) => html`
<input type="checkbox" checked="${checked}" ${dynamicAttr} />
`;
render(tpl({
checked: true,
dynamicAttr: 'disabled'
}), container);
If target element has a property
of attribute name, then value will be set as that property. This makes possible to pass values other than primitive ones
Promise values (demo)
Values can be promises. In such case attribute value will be set once promise is resolved
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = ({ checked }) => html`
<input type="checkbox" checked=${checked} />
`;
render(tpl({
checked: new Promise((resolve) => setTimeout(() => resolve(true), 2000))
}), container);
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const tpl = ({ style }) => html`
<div style=${style}>
hello world
</div>
`;
render(tpl({
style: {
border: '1px solid #ddd',
borderRadius: '3px',
}
}), container);
If attribute key is a function, it will be called with parameters target, attributeValue
. E.g.:
html`
<input ${(target, value) => {
//target === <input element>
//value === 'test'
}}="test">
`
Following example shows such case:
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
const on = (eventName) => ($el, callback) => {
$el.addEventListener(eventName, callback);
};
const handler = (event) => console.log(event.target);
const tpl = (handler) => html`
<span ${on("click")}="${handler}">click me</span>
`;
render(tpl(handler), container);
until(promise, placeholderContent)
(demo)
Renders placeholderContent
until promise
resolves
Renders content
as html
Custom Elements (demo)
modulor-html
works perfect with native custom elements and their existing polyfills.
tested with https://github.com/webcomponents/webcomponentsjs and https://github.com/WebReflection/document-register-element
import { html, render } from '@modulor-js/html';
const container = document.querySelector('#container');
customElements.define('my-component', class extends HTMLElement {
constructor(){
super();
console.log('constructor');
}
connectedCallback(){
console.log('connected');
}
set prop(val){
console.log(`prop set to: ${val}`);
}
static get observedAttributes() {
return ['attr'];
}
attributeChangedCallback(name, oldValue, newValue) {
console.log(`attribute ${name} set to: ${newValue}`);
}
});
const tpl = (scope) => html`
<my-component prop="${scope.prop}" attr="${scope.attr}"></my-conponent>
`;
render(tpl({ prop: 'foo', attr: 'bar' }), container);
//"constructor"
//"prop set to: foo"
//"attribute attr set to: bar"
//"connected"
-
templates must have valid html markup
-
attribute value can be set without quotes (
<input type="${type}" />
==<input type=${type} />
) -
self-closing tags (except for ones from this list) are not supported yet
-
IE shuffles attributes in a strange manner so execution order might be unexpected (this is important to know when using
CustomElements
) -
calling
render
without second attribute generatesDocumentFragment
out of template