Data Binding
Data binding is a process of connecting the application state to the user interface. If the connection is successful, data changes will be reflected in the UI and user actions will be properly executed. There are multiple ways of applying data to the widgets.
Two-way Data Binding (-bind)
Two-way data binding is commonly used in forms as it supports both read and write operations. Let's use a checkbox for illustration. To display a checkbox we need to know whether it's checked or not. That's the read operation. If the user clicks the checkbox the corresponding value needs to be changed. This makes the write operation.
Suffix
-bindis used on attributes to define two-way bindings.
Data Expressions (-expr)
Data expressions are string attributes that are compiled into JavaScript methods used to calculate dynamic
values at runtime. Let's add a new text field and use a data expression in the enabled property.
Note that now the text field and the checkbox from the previous section
are both using the value intro.core.checked.
Try clicking the checkbox and you will see how it works together with the text field.
Suffix
-expris used on attributes to define data expressions.
Curly brackets denote data bindings.
Data bindings pointing to invalid locations will be reported as
undefined.
Templates (-tpl)
Templates are data expressions which return strings. They are a convenient option to avoid using both types of quotes within data expressions.
<div layout={LabelsLeftLayout}>
<TextField value-bind='intro.core.firstName' label="First Name" />
<TextField value-bind='intro.core.lastName' label="Last Name"/>
<TextField value-tpl='Hello {intro.core.firstName} {intro.core.lastName}!' label="Template" mode="view"/>
<TextField value-expr='"Hello "+{intro.core.firstName:s}+" "+{intro.core.lastName:s}+"!"' label="Expression" mode="view"/>
</div>Templates support formatting.
Function Bindings (Selectors)
A complicated calculation is sometimes required which is not suitable for a simple data expression. In this case, a function can be assigned to an attribute.
This approach may cause performance problems and should be done with caution. Remember that:
- Selector functions take only one argument, which is the whole app state object.
- Selector functions are invoked on each render pass.
- Selector functions must be deterministic.
- Unlike data expressions, selector functions are not memoized. It is up to the developer to add memoization for expensive calls.
- Deeply nested state is not guaranteed to be initialized (e.g. access to
intro.core.letterCountmay fail).
import { computable } from 'cx/data';Computables overcome shortcomings of function bindings by offering memoization and easier access to deeply nested data.
Setters are used in combination with expressions and computables to provide write operations. A setter is a function used to process user input and write it to the store.
Throttle / Debounce
Sometimes you want to limit the rate of propagating changes or to postpone
the change until the user finishes interaction. In such scenarios throttle
and debounce come in very handy.
<div>
<Section class="well" title="Direct">
<Slider value-bind="$page.slider.direct" />
<Slider value-bind="$page.slider.direct" />
</Section>
<Section class="well" title="Throttle: 300ms">
<Slider value={{ bind: "$page.slider.throttled", throttle: 300 }} />
<br />
<Slider value={{ bind: "$page.slider.throttled", throttle: 300 }} />
</Section>
<Section class="well" title="Debounce: 300ms">
<Slider value={{ bind: "$page.slider.debounced", debounce: 300 }} />
<br />
<Slider value={{ bind: "$page.slider.debounced", debounce: 300 }} />
</Section>
</div>