Documentation

Report Edit

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.

Code
<Checkbox value-bind='intro.core.checked'>Checkbox</Checkbox>
Copied!Cx Fiddle

Suffix -bind is 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.

Code
<TextField
    value-bind='intro.core.text'
    enabled-expr='!{intro.core.checked}'
/>
Copied!Cx Fiddle

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 -expr is 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.

Hello !
Hello !
Code
<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>
Copied!Cx Fiddle

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.

You typed letter A 0 times.

Code
store.set('intro.core.letterCount', '');
...
<TextField value-bind='intro.core.letterCount' placeholder="Type here" />
<p>
    <Text value={(storeData) => `You typed letter A
    ${((storeData.intro.core.letterCount || '').match(/A/g) || []).length} times.` } />
</p>
Copied!Cx Fiddle

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.letterCount may fail).
import { computable } from 'cx/data';

Computables overcome shortcomings of function bindings by offering memoization and easier access to deeply nested data.

+
= ERR
Code
<div preserveWhitespace>
    <NumberField value-bind='intro.core.a' placeholder="A" />
    +
    <NumberField value-bind='intro.core.b' placeholder="B" />
    =
    <Text value={computable('intro.core.a', 'intro.core.b', (a, b) => a==null || b==null ? "ERR" : a + b )} />
</div>
Copied!Cx Fiddle

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.

A + 2 =

A =
Code
<div preserveWhitespace>
    A + 2 = <NumberField
                style="width:50px"
                value={{
                    expr: '{intro.core.a}+2',
                    set: (value, {store}) => { store.set('intro.core.a', value - 2) }
                }}
            />
    <br/>
    A = <Text bind="intro.core.a" />
</div>
Copied!Cx Fiddle

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.

Direct
 

 
Throttle: 300ms
 

 
Debounce: 300ms
 

 
Code
<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>
Copied!Cx Fiddle