Markup your server already rendered
applyBindings(viewModel, element) walks existing HTML and activates its binding attributes in place. It never
rewrites the page: the server's text stays until the data says otherwise, so there is no flash of empty content and
nothing to hydrate.
The view model, live
{}
Caret in the name field: false
Thank you. Nothing is actually sent.
<div class="demo-stage demo-split">
<div class="demo-form">
<label class="form-label">Name
<input class="form-input" data-model="person.name.value" data-focus="focused.value">
</label>
<label class="form-label">Age
<input class="form-input" type="number" min="0" max="120" data-model="person.age.value">
</label>
<label class="form-label">Home town
<select class="form-select" data-model="person.town.value" data-options="towns" data-options-caption="'Anywhere'"></select>
</label>
<fieldset class="demo-fieldset">
<legend>Contact by</legend>
<label><input type="radio" name="contact" value="email" data-model="person.contact.value"> Email</label>
<label><input type="radio" name="contact" value="phone" data-model="person.contact.value"> Phone</label>
<label><input type="radio" name="contact" value="post" data-model="person.contact.value"> Post</label>
</fieldset>
<label class="demo-switch">
<input type="checkbox" data-model="person.newsletter.value"> Send me the newsletter
</label>
</div>
<div class="demo-mirror">
<p class="demo-mirror-title">The view model, live</p>
<pre class="demo-json" data-bind-text="asJson.value">{}</pre>
<p class="demo-note">Caret in the name field: <b data-bind-text="focused.value">false</b></p>
<p class="demo-note" data-if="person.newsletter.value">Thank you. Nothing is actually sent.</p>
</div>
</div>/**
* Two-way binding, with no change handler anywhere.
*
* Text, number, radio group, checkbox and a `<select>` populated by
* `data-options` all write straight back into the view model; `data-focus`
* reports where the caret is. The JSON panel is a `computed` reading the same
* observables the inputs write to, so it cannot disagree with them.
*/
function bindings() {
const person = {
name: observable('Ada Lovelace'),
age: observable(36),
town: observable('Harrogate'),
contact: observable('email'),
newsletter: observable(false)
};
const focused = observable(false);
return {
person,
focused,
towns: ['Harrogate', 'Knaresborough', 'Ripon', 'Wetherby', 'Otley', 'Ilkley'],
asJson: computed(() => JSON.stringify({
name: person.name.value,
age: person.age.value,
town: person.town.value || null,
contact: person.contact.value,
newsletter: person.newsletter.value
}, null, 2))
};
}The twelve kinds
| Attribute or tag | Does |
|---|---|
data-bind-text, data-bind-<attr> |
Text, any attribute or DOM property (disabled, hidden, checked...) |
data-bind-class |
Adds and removes only the classes it set, so static classes survive |
data-bind-style-<prop>, data-bind-style |
One CSS property, or an object of them |
data-model |
Two-way: text, number, checkbox, radio, select and select multiple |
data-on-<event> |
Any DOM event; returning false prevents the default |
data-if |
The element is in the document or it is not - the same node comes back |
data-each="rows key=id" |
A keyed list - see lists |
data-options |
Fill a <select> from data |
data-focus |
Two-way between a value and which element has focus |
data-component |
Render a registered component - see components |
{{ }}, {{#if}}, {{#each}} |
Mustache, inside templates and list bodies |
<!-- dm if: x -->, <!-- dm each -->, <!-- dm text --> |
Virtual bindings, for markup with no element to spare |
There is no data-bind-html, on purpose: assigning innerHTML from data is the shortest route to a cross-site
scripting hole. {{{triple-stache}}} in a template is the explicit, greppable opt-out.
Virtual bindings
A table row or a run of list items has no element to spare for an attribute. A comment does:
<ul>
<li>Always shown</li>
<!-- dm if: showExtras.value -->
<li>Only when showExtras</li>
<!-- /dm -->
</ul>
The tutorial's contacts page uses <!-- dm text --> and <!-- dm if --> for its summary line and empty
state.