Template DSL reference
The whole reactive vocabulary of ReactiveComponent, in one place.
Any path used in data-if, data-else-if
or as a data-class value accepts a leading !
to negate it (e.g. data-if="!loading").
| Attribute | Where | Syntax | Effect |
|---|---|---|---|
data-bind | any element | {"attribute":"path"} | Two-way attribute/property binding |
data-class | any element | {"class":"boolean.path"} | Toggles CSS classes |
data-style | any element | {"css-property":"path"} | Sets inline style properties |
data-on | any element | {"event":"method"} | Calls this.method(event, scope) when the event fires |
{{ path }} | text nodes | Hello {{ user.name }} | Text interpolation (one-way); undefined/null render as empty, objects as JSON |
data-ref | any element | "name" | Exposes the element on this.refs.name |
data-if | <template> | "path" | Starts a conditional chain |
data-else-if | <template> | "path" | Continues the conditional chain |
data-else | <template> | (no value) | Unconditional fallback |
data-for | <template> | "item in path [: key]" | Repeats the content for each item of an array |
data-bind
Attribute or property binding, both ways. In the data → DOM direction, the value read from the
path is applied following a sequence of special cases — textContent,
checked (for checkboxes), value,
datetime-local fields — before falling back to a generic
setAttribute.
<input data-bind='{"value":"name"}'>
<input type="checkbox" data-bind='{"checked":"active"}'>
<span data-bind='{"textContent":"name"}'></span>
In the DOM → data direction, any element with data-bind listens for
the native change event and writes the value it reads back to the
corresponding path — that's how an <input> becomes a two-way
binding, with no extra syntax.
The same syntax composes nested reactive components: data-bind='{"data-state":"path"}'
on a <child-component> passes a whole object down into it, both ways.
A light-DOM child renders its own content with its own state: the parent only binds the child
element itself, not what's inside it.
Boolean properties (disabled, checked,
hidden, required, …) are driven through the
property, so false really means off. A null or
undefined value clears the attribute (or empties value
and textContent) instead of printing "undefined". In a list of primitives
(n in nums), binding to the item variable writes back into that slot of the array.
on*) and srcdoc are never bound, and URL attributes
(href, src, action, …) refuse
javascript:, vbscript: and
data:text/html values (a warning is logged). Malformed JSON in
data-bind/data-class/data-style
or in data-state is reported with console.error and ignored.
data-class
<div data-class='{"highlight":"active", "error":"!valid"}'></div>
Toggles each class via classList.toggle, based on the (boolean, or
negated with !) value of the corresponding path.
data-style
<div data-style='{"border-color":"statusColor"}'></div>
Sets an inline style property from the path's value. If the value is
undefined/null, the property is removed.
data-on
<button data-on='{"click":"save"}'>Save</button>
<template data-for="task in tasks : id">
<button data-on='{"click":"remove"}'>x</button>
</template>
// in the component class
remove(event, scope) {
this.state = { ...this.state, tasks: this.state.tasks.filter(t => t.id !== scope.task.id) };
}
Maps native event names to methods of the component (never to arbitrary code). The
method receives the event and the element's read scope: state, computed values and, inside a
data-for, the loop variables (scope.task above). A name that
isn't a method is reported with console.error. One listener per element and
event is created for the element's whole life, so re-rendering never stacks handlers, and
keyed list items always call the handler with their current item.
Text interpolation — {{ path }}
<p>Hello {{ user.name }}, you have {{ count }} items</p>
<template data-for="t in tasks"><li>{{ t.title }}</li></template>
Inside any text node, {{ path }} is replaced by the value at that path
(same path syntax as the other bindings, including the leading ! and the
data-for variables). It is one-way and the value is always inserted as
text, never as HTML. It is not evaluated inside <script>,
<style>, <textarea> or in an element whose
data-bind sets textContent/innerHTML.
Only paths are supported, not arbitrary expressions: use computed() for those.
data-ref
<input data-ref="searchField">
// after rendering:
this.refs.searchField.focus();
Exposes the element on this.refs.name. If more than one element uses
the same name (common inside a list), this.refs.name becomes an array.
Conditionals — data-if / data-else-if / data-else
<template data-if="loading">
<p>Loading...</p>
</template>
<template data-else-if="error">
<p>Something went wrong.</p>
</template>
<template data-else>
<p>All good.</p>
</template>
A chain is a sequence of consecutive sibling <template>
elements, starting with data-if, with zero or more
data-else-if, optionally ending in a single
data-else. The first truthy path wins.
data-for inside the branch: item identity
survives renders in which the condition didn't change.
Lists — data-for
<template data-for="task in tasks : id">
<li data-bind='{"textContent":"task.title"}'></li>
</template>
Syntax: "item in path"; optionally
"item, index in path" (also exposes the index); and a
" : field" suffix that declares the items' identity key.
With a usable key — explicit, or inferred when every item has a uuid
or id — the list does keyed reconciliation: the DOM
nodes of an item whose key is still present are reused and only repositioned, not recreated.
Without a key (or with a missing/duplicate key in a given pass), the whole list is rebuilt on
every render.