jComponent / 08. Data Binding
Updated: 24. August 2020
Author: Peter Širka

08. Data Binding

Professional Support Chat with contributors

Data-Binding is a special feature in jComponent. It can affect DOM element dynamically.

Topics:


Declaration:

<div data-bind="path.to.property__command1:exp__command2:exp__commandN:exp"></div>

Commands

command: attr

+v18 command sets a value into the custom attribute:

<div data-bind="some.path__attr data-id"></div>
<div data-bind="some.path__attr data-id:value"></div>

<!-- With nested selector: -->
<div data-bind="some.path__attr data-id b:value"></div>

command: changes

+v18 command performs all other commands only if the value will be changed. It works with object types too.

<div data-bind="some.path__changes__html"></div>

command: checked

Sets the checked attribute. Targeted for HTML controls only input. Expression must return Boolean.

<input data-bind="some.path__checked:value" />

command: class

Toggles specified class when the data-bind is processed. Epression must be String only. Supports multiple classes seperated by empty space.

<div data-bind="some.path__class:hidden" class="hidden">READY</div>

<!-- Or multiple classes: -->
<div data-bind="some.path__class:hidden ready" class="hidden">READY</div>

command: click

+v17 Executes method when the user clicks on the element.

<div data-bind="some.path__click:link_to_method"></div>

<!-- OR WITHOUT PATH -->
<div data-bind="null__click:link_to_method"></div>

<!-- Replaces ? for a SCOPE PATH -->
<div data-bind="null__click:?/link_to_method"></div>

<script>
    function link_to_method(element, event, value, path) {
        // do something
    }
</script>

command: config

Performs reconfiguration for the current jComponent. Expression must return String with jComponent configuration or can return Object.

<div data-bind="some.path__config:'required:' + (value === true)" data---="textbox">My textbox</div>

<!-- Sets "required" for all nested jComponents -->
<div data-bind="some.path__config [data-jc]:'required:' + (value === true)">
    <div data---="textbox"></div>
    <div data---="dropdown"></div>
    etc..
</div>

command: currency

+v18 Renders formatted currency. A value in currency command is a currency, it's not evaluated and can be used [value_from_ENV].

<div data-bind="some.path.to.number__currency:eur__html"></div>

Additional example with a dynamic currency:
<div data-bind="some.path__html:value.price.currency(value.currency)__track:price,currency"></div>

command: def

Sets the def attribute. It's targeted for HTML controls only input, textarea, select and button.

String:
<div data-bind="some.path__def:'String'__html:JSON.strignify(value)"></div>

Number:
<div data-bind="some.path__def:100__html:JSON.strignify(value)"></div>

Boolean:
<div data-bind="some.path__def:true__html:JSON.strignify(value)"></div>

Object:
<div data-bind="some.path__def:{}__html:JSON.strignify(value)"></div>

Array:
<div data-bind="some.path__def:[]__html:JSON.strignify(value)"></div>

command: delay

Delays a binding for 2 seconds. Must be a number which represents milliseconds.

<input data-bind="some.path__val:value__delay:2000" />

command: disabled

Sets the disabled attribute. It's targeted for HTML controls only input, textarea, select and button. Expression must return Boolean.

+v17 extends disabled command for components. Now the library can perform reconfigure with disabled:true/false command.

<input data-bind="some.path__disabled:value" />

command: empty

+v17 Sets an empty HTML to the current element when is the value null or undefined or empty string.

<div data-bind="some.path__empty:---"></div>

command: enabled

+v16 Sets the enabled attribute. It's targeted for HTML controls only input, textarea, select and button. Expression must return Boolean.

+v17 extends enabled command for components. Now the library can perform reconfigure with disabled:true/false command.

<input data-bind="some.path__enabled:value" />

command: exec

Expression must be a link to a function. This function will be executed if the data-bind path will be changed.

<div data-bind="some.path__exec:myfunction"></div>

<script>
    function myfunction(value, path, element) {
        console.log('VALUE HAS BEEN CHANGED:', value);
    }
</script>

command: focus

+v18 supports focus command which can contain a jQuery selector which will perform .focus(). Example:

<div data-bind="some.path__focus:input">
    <input type="text" value="Focused!" />
</div>

command: format

+v17 enables a specific format for Date or Number types.

<div data-bind="some.path.date__html__format:dd.MM.yyyy"></div>

// Or link to environment variable
<div data-bind="some.path.date__html__format:[date]"></div>

// +v17 supports path to value in the form of environment variables
<div data-bind="some.path.date__html__format:[.path.to.property]"></div>

command: hide

Toggles hidden class. Expression must return Boolean.

<div data-bind="some.path__hide:value"></div>
<div data-bind="some.path__hide:value !== 'users'"></div>

command: href

Sets href attribute. It's targeted for links <a> only. Expression must return String.

<div data-bind="some.path__href:value"></div>

command: html

Sets the value as the HTML content. Expression must return String.

<div data-bind="some.path__html:value"></div>
<div data-bind="some.path__html:value.toUpperCase()"></div>

command: init

+v17 Executes method when the binder is initialized. This command is performed only one time.

<div data-bind="some.path__init:link_to_method"></div>

<script>
    function link_to_method(value, path, element) {
        // do something
    }
</script>

command: import

Performs IMPORT(). Expression must be URL address String or (+v16) value must return a valid URL adress.

This command imports each resource only one time.

<div data-bind="some.path__import:https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></div>

<div data-bind="some.path__import:value"></div>

+v18 adds a support for <script type="text/html">:

<div data-bind="some.path__show__import:true">
    <script type="text/html">
        The content will be evaluated when the element will be visible.
    </script>
</div>

+v18 supports ENV

<script>
    ENV('mycdn', 'https://totaljs.com');
</script>

<div data-bind="some.path__import:[mycdn]/js/app.js"></div>

command: invisible

+v16 sets invisible class to this element if the condition is valid. invisible class is a part of spa.min.css and it's declaration is .invisible { visibility: hidden; }. Usage:

<div data-bind="some.path__invisible:value == true"></div>

command: refresh

+v18 Expression must be a link to a function. This function will be executed if the element is visible and the data-bind path will be changed.

<div data-bind="some.path__refresh:myfunction"></div>

<script>
    function myfunction(value, path, element) {
        console.log('VALUE OF VISIBLE ELEMENT HAS BEEN CHANGED:', value);
    }
</script>

command: resize

+v18 executes resize method in all nested components. IMPORTANT: the element must visible.

command: required

+v18 performs required reconfiguration for the current element component. It expects boolean value type.

<div data---="input__?.company" data-bind="?.iscompany__required">Company name</div>

command: set

+v18 can set a value to a component. It will works with vbindarray too, but only in readonly mode.

  • . dot in component's path allows to bind a value from data-bind
  • set command is very important for binding
<div data-bind="some.path__set" data---="textbox__.__required:1"></div>

OR

<div data-bind="some.path__set:value.toUpperCase()" data---="textbox__.__required:1"></div>

command: setter

+v16 performs element.SETTER() for nested components.

<div data-bind="some.path__setter:'datagrid','resize'"></div>
<!-- performs: element.SETTER('datagrid', 'resize') -->

<div data-bind="some.path__setter:true,'datagrid','resize'"></div>
<!-- performs: element.SETTER(true, 'datagrid', 'resize') -->

command: show

Toggles hidden class. Expression must return Boolean.

<div data-bind="some.path__show:value"></div>
<div data-bind="some.path__show:value === 'users'"></div>

command: src

Sets src attribute, targeted for <img. Expression must return String.

<div data-bind="some.path__src:value"></div>

command: strict

This command performs a strict comparing of path. Must be without expression.

<div data-bind="myobj.address__strict__html:JSON.stringify(value)"></div>
  • allowed paths myobj or myobj.address
  • another paths like myobj.customer or something else doesn't affect this binder

command: template

Compiles HTML content as a Tangular template. Can be defined without expression. +v17 expression can contain a jQuery selector which enables Virtual DOM comparing for content.

<div data-bind="some.path__template">
    <script type="text/html">
        <div>{{ value.name }}</div>
        <div>{{ value.price | format(2) }}</div>        
    </script>
</div>

<!-- +v17 With Virtual DOM differ -->
<div data-bind="some.path__template:.product">
    <script type="text/html">
        <div class="product">
            <div>{{ value.name }}</div>
            <div>{{ value.price | format(2) }}</div>        
        </div>
    </script>
</div>

<!-- +v18 With custom template -->
<div data-bind="some.path__template:{jQuery selector}"></div>

<!-- +v18 With custom template and virutal dom -->
<div data-bind="some.path__template:.product {jQuery selector}"></div>
  • value represents Tangular model

Virtual DOM: Template is compiled to Virtual DOM and then it's compared with current DOM. This feature can improve rendering (blinking) when the content is re-rendering.

command: text

Sets a value as the TEXT content (HTML markup will be escaped). Expression must return String.

<div data-bind="some.path__text:value"></div>
<div data-bind="some.path__text:value.toUpperCase()"></div>

command: title

Sets title attribute.

<div data-bind="some.path__title:value"></div>

command: track

Enables a strict path comparison for defined paths separated by comma (use a field name without parent path). Binder will render the content below if the main path object will be changed or specific field (name or price) will be changed.

<div data-bind="some.path__template__track:name,price">
    <script type="text/html">
        <h1>{{ value.name }}</h1>
        <div>Price: {{ value.price | format(2) }} EUR</div>
    </script>
</div>

command: tracktype

Tracks only changes according to defined SET() types. A value can contain multiple values separated by comma.

<div data-bind="some.path__html__tracktype:2"></div>

<script>
    // Will be binded
    SET('some.path', 'Value', 2);

    // Won't be binded
    SET('some.path', 'Value', 1);
</script>

Internal types:

  • 0 init value
  • 1 manually via e.g. SET()
  • 2 by input
  • 3 default value

command: val

Sets value for the input/textarea/select. Expression must return String.

<input data-bind="some.path__val:value" />

command: vbindarray

+v17 This is very special command. It has same functionality like template but the <script type="text/html" must contain VBINDARRAY template. Read more about VBINDARRAY here.

<div data-bind="some.path.to.array__vbindarray">
    <script type="text/html">
        <div data-bind=".name__html b">Name: <b></b></div>
        <p data-bind=".description__html"></p>
    </script>
</div>

command: visible

+v16 sets invisible class to this element if the condition is not valid. invisible class is a part of spa.min.css and it's declaration is .invisible { visibility: hidden; }. Usage:

<div data-bind="some.path__visible:value !== false"></div>

command: .class_name

Toggles .class_name if the expression returns true. You can use multiple .class_name commands.

<div data-bind="some.path__.animate:value > 100"></div>

Multiple:
<div data-bind="some.path__.animate1:value > 100__.animate2:value > 1000"></div>

Types of command expressions

You can use multiple types of command expressions. You need to choose what suits is for you. In most cases value argument contains the real value from a model according to the data-bind path. IMPORTANT: click command uses other order of arguments.

Arrow function in the form:

COMMAND:(value,path,jQueryElement)=>value.toUpperCase()

<or>

COMMAND:n=>n.toUpperCase()

Direct value:

COMMAND:value.toUpperCase()

<or>

COMMAND:value

<or>

COMMAND:value > 10

Link to method:

COMMAND:upper_value
  • but function upper_value(value, path, element) must be defined in the window scope

Advanced usage

Good to know

+v16 Most of commands are performend when the element is visible --> in other words: the command show must return true or hide must return false.

If you want to evaluate a command for hidden element then you can extend command by adding ~ before the name of command, for example:

<div data-bind="some.path__~setter:'datagrid','resize'">
    ...
</div>

Linking commands to same expression

You can link commands to same expression with help of + char with spaces on both sides.

<div data-bind="path.to.property__COMMAND + COMMAND + COMMAND:VALUE"></div>
<div data-bind="user.age__visible + .selected:age => age > 18__html:value"></div>

Extending command by adding a selector

You can extend most of commands by adding a custom jQuery selector.

Declaration:
<div data-bind="some.path__html JQUERY_SELECTOR:value"></div>

Usage:
<div data-bind="some.path__html h1:value__show:value">
    <h1></h1>
</div>

Nullable values

Nullable / Undefined values can be handled easily. Just use !COMMAND and evaluating will be performed if the value won't be null or undefined.

<div data-bind="user.name__!html:value.toUpperCase()"></div>

Multiple watchers

You can define multiple data-bind paths in the same HTML element. Paths must be divided by this | phrase. For example:

<div data-bind="path.to.property1__command:expression__|__path.to.property2__command:expression__|__path.to.property3__command:expression"></div>

jComponent scopes

You can use data-bind in jComponent scopes, but you need to defined ? (question mark) on the start of data-bind path. Question mark ? will be replaced for a scope path.

<div data-bind="?.property__html:value"></div>

+v17 supports scope in command values:

<div data-bind="?.property__html:?/render"></div>
<!-- WILL BE COMPILED TO -->
<div data-bind="SCOPEPATH.property__html:SCOPEPATH/render"></div>

Good to know:

  • it will work only if the jComponent scope will contain some jComponents

Inline helpers

Data-Binding supports inline helpers:

1. Direct assignment
<div data-bind="form.name --> (value || '').toUpperCase()__html:value"></div>

2. With arrow function
<div data-bind="form.name --> n => (n || '').toUpperCase()__html:value"></div>

3. Plugins
<div data-bind="form.name --> plugin/method(value)__html:value"></div>

Using data-bind in jComponents

The example below will work in component's scope only.

Binds a value according to the data-jc-path attribute.
It works in the component scope only.

<div data-bind="@__COMMAND + COMMAND + COMMAND:VALUE"></div>

Binds a component config.
It works in component scope only.

<div data-bind="@config__COMMAND + COMMAND + COMMAND:VALUE"></div>

Binds a value defined via component.data('property', value).
It works in the component scope only.

<div data-bind="@property__COMMAND + COMMAND + COMMAND:VALUE"></div>

+v17 supports private binding methods:

<div data-bind="@__html:@method_in_component"></div>

Less code in Data Binding

+v17 supports shorter commands:

<div data-bind="property__show"></div>
<!-- IS SAME AS -->
<div data-bind="property__show:value"></div>

<div data-bind="property__html"></div>
<!-- IS SAME AS -->
<div data-bind="property__html:value"></div>

<div data-bind="property__html__show"></div>
<!-- IS SAME AS -->
<div data-bind="property__html:value__show:value"></div>

Disable Data Binding

+v17 supports disabling of data binding:

<div id="myelement" data-bind="property__html:value"></div>

<script>
    $('#myelement').binder().disabled = true;
</script>

Refreshing Data Binding

+v17 supports refreshing of data-bind. It can be helpful for specific components and behaviours.

<div id="myelement" data-bind="property__html:value"></div>

<script>
    $('#myelement').binder().refresh();
</script>

Virtual binder: VBIND

+v16 supports this method. Virtual binder VBIND is a special thing and it can bind data from a custom model which doesn't need to be defined in the window scop. Compiled element doesn't need to be a part of DOM.

  • paths must start with (dot) .path
  • it expects a custom object
// VBIND(template);
var obj = VBIND('<div data-bind=".name__html:value"></div>');

// Sets a model
obj.set({ name: 'Peter' });

// Sets a specific value with path
obj.set('name', 'Peter');

// +v18 Updates a specific path
obj.upd(path);

// +v18 Updates entire object
obj.upd();

// Removes the binder
obj.remove();

// jQuery element
obj.element.html(); // <div>PETER</div>

// Attachs element into the DOM
$(document.body).append(obj.element);

+v17 supports private binding methods:

var obj = VBIND('<div data-bind=".name__html:.mymethod"></div>');

obj.mymethod = function(value, path, el) {
    // this === jQuery element
    return 'VALUE_FOR_COMMAND';
};

// Sets a model
obj.set({ name: 'Peter' });

Obtaining VBIND instance from element

var vbind = element.vbind();
vbind.set(YOUR_OBJECT);

Virtual binder: VBINDARRAY

+v16 supports this method. VBINDARRAY prepares a template as VBIND object and it renders items/templates very effective according to the array. It's someting similiar like Virtual DOM.

  • paths must start with (dot) .path in the template
  • each VBIND element will contain data-index attribute with the item array index
// VBINDARRAY(template, target_element);
var obj = VBINDARRAY('<div data-bind=".name__html:value"></div>', document.body);
obj.set([{ name: 'Peter' }, { name: 'Anna' }, { name: 'Lucia' }]);

+v17 supports private binding methods:

var obj = VBINDARRAY('<div data-bind=".name__html:.mymethod"></div>', document.body);

obj.mymethod = function(value, path, el) {
    // this === jQuery element
    return 'VALUE_FOR_COMMAND';
};

// Possibilities:
// obj.set(index, item);
// obj.set(array);

// Sets a model
obj.set([{ name: 'Peter' }, { name: 'Anna' }, { name: 'Lucia' }]);

// v18 additional methods:
// obj.upd(index);
// obj.upd();

Obtaining VBINDARRAY instance from element

var vbindarray = element.vbindarray();
vbindarray.set([YOUR_ARRAY]);