Declarative data binding
liaison's declarative data binding is based on <template> element, one of the specs in Web Components umbrella.
<template> element creates an inert DOM tree based on its content. liaison adds two-way data binding feature to <template>, which can be used by loading liaison/DOMTreeBindingTarget module and then calling HTMLTemplateElement#bind() API.
HTMLTemplateElement#bind() API creates one or multiple copies (called "instances") of <template>'s DOM tree, and can be used in one of the following ways:
- To create a single instance based on data model:
HTMLTemplateElement#bind("bind", observable) - To create repeating instances based on an array of data model:
HTMLTemplateElement#bind("repeat", observableArray)
Let's take a look at the first one. If you have <template> like below:
You can instantiate it by assigning a data model by below code for example:
You can do the repeating one in a similar manner:
Note: wrapper.wrap() API creates a tree of "container object", called Observable and ObservableArray. "Container object" approach allows event-driven change noitification instead of dirty-checking, which ensures the best performance especially for large-scale applications with many watched objects. In environment where ES7 Object.observe() and Array.observe() are available natively, liaison's two-way data binding features work with plain JavaScript object/array.
<template> content is instantiated as siblings of <template>. Here's the (explanatory) state of DOM after repeating template instances are created (2nd example):
<template id="my-template">
<div>
First: <input type="text" value="{{first}}">
Last: <input type="text" value="{{last}}">
</div>
</template>
<div>
First: <input type="text" value="Anne">
Last: <input type="text" value="Ackerman">
</div>
<div>
First: <input type="text" value="Ben">
Last: <input type="text" value="Beckham">
</div>
<div>
First: <input type="text" value="Chad">
Last: <input type="text" value="Chapman">
</div>
<div>
First: <input type="text" value="Irene">
Last: <input type="text" value="Ira">
</div>
Nested template
<template> can be nested with <template bind="{{observable}}"> and/or <template repeat="{{observableArray}}"> syntaxes, which correspond to template.bind("bind", observable) and template.bind("repeat", observableArray) respectively. For example, you can do:
Two-way data binding
liaison's data binding is two-way in many cases. With two-way binding, change in data model is reflected to UI, and change in UI is reflected back to model. In below example, change in <input> automatically updates data model. Update in data model automatically updates the text underneath:
See here to see in what cases two-way binding is supported.
In addition to two-way data binding between model property and UI, repeating template instances reflects array splices in model:
Updating array element with particular index (e.g. model.set(1, wrapper.wrap({first: "John", last: "Jacklin"}))) as well as push()/pop(), etc. are translated to splices and are reflected to repeating template instances.
More ways to work with template
liaison's declarative data binding provides more ways to work with <template>. See here for details.
