In a nutshell, data binding is a technique that allows you to connect components establishing master-slave relations between them. The technique supposes that the master component serves as the data source for the slave(s) when any record or value is selected in the master. Thus the master must always have a DataStore or DataValue. The slave can be a single-record component (e.g. form or template) or a data component as well.
To bind two components you need to call the bind method on the slave component, passing the master one as a parameter. One of the common use cases is to bind a form to a data component e.g. List:
$$("form1").bind($$("list1"));
Now when you select an item from the list, the form will be populated with the item data. Keep in mind that the name property of the input fields must coincide with the data field names in the data component:
// data component
{ name:"Jane", age:23 }
// form
{
view:"form", id: "form1",elements:[
{ name:"name", view:"text" },
{ name:"age", view:"text", type:"number" }
]
}
Changes in the slave component will affect the master. If you edit the data in the form and then save it, the list item will be updated. To save the changes, call the save method of the form. If the form was not filled with the data from the master when the save method was called, the form will add a new element into the master.
Note that only bound forms have this method:
$$("form1").save();
Related sample: Creating Basic App: Step 3
Related sample: Binding to a Native HTML Form
You can also save some extra data together with the values from the form. For that you should get the form values, add necessary properties there and pass the resulting object to the form.save() method:
const values = form.getValues();
values.myfield = "My value";
form.save(values);
If you no longer want to connect the bound views (components), you can unbind the slave component:
$$("form1").unbind();
You can think of binding as syntactic sugar for a more straightforward approach to connect components via events. For instance, the above example of binding form to a list can be reproduced with:
// select an item in the list and fill the form
$$("list1").attachEvent("onAfterSelect", function(id){
var item = this.getItem(id);
$$("form1").setValues(item);
});
// save form data into the list
view:"form", id:"form1", elements:[
// ...
{
view:"button", value:"Save", click:function(){
var item = this.getFormView().getValues();
$$("list1").updateItem(item.id, item);
}
}
]
Consider this approach, if you develop with Webix Jet, as a form and its master are likely to be in the different files so classic binding is impossible.
Binding two forms to the same master component is an uncommon practice, yet it is possible. By default, if you bind two forms to the master and try to save each of them separately, the data from the previously saved one will be lost.
There are two possible solutions:
const v1 = $$("form1").getDirtyValues();
const v2 = $$("form2").getDirtyValues();
$$("form1").save(webix.extend(v1, v2, true) ); // combine values from both forms
$$("datatable1").saveBatch(function(){
$$("form1").save();
$$("form2").save($$("form2").getDirtyValues());
});
Related sample: Binding Two Forms to the Same Master
The getDirtyValues method returns only the fields that were changed in the form.
This concerns only those slave data components that have the template property in their configuration. In a form and html form binding is set according to the names of their inputs.
When no selection is made in the master component, the slave component shows undefined values. You can set the default values that should be displayed when nothing is selected. Use the defaultData property of the master:
webix.ui({
rows:[
{
view:"list",
id:"list1",
template:"#rank#.#title#",
// data: ...
defaultData:{
rank:"0",
title:"default Item"
}
},
{ view:"template", id:"template1" template:"#rank#.#title#" }
]
});
$$("template1").bind($$("list1"));
Related sample: Binding: Default Data
As a rule, a slave component does not communicate with the server side directly. It receives data from its master, and then the changed data are sent first to the master that handles communication with the server. However, you can get the server-side data for the slave based on master selection with the help of the dataFeed functionality.
webix.ui({
view:"form",
id:"form1",
// ...config
dataFeed: "slave_data.php"
});
$$("form1").bind("datatable1")
Related sample: Datatable: Binding with Form
dataFeed defines the URL that will be used by the slave to send a request when selection in the master component changes.
The functionality works the same for slave forms and collections (data components), yet the URL parameters sent with a request differ:
where obj is the selected data item in the master component.
The master can be a single-value component like Richselect, Datepicker, etc. When it changes its value, the value is passed to the slave.
$$("template1").bind($$("calendar1"));
Related sample: Data Binding: Template Bound to Calendar
In this case, binding is possible only if the slave component has DataStore or TreeStore (DataTable, Tree, List, Chart, etc).
When a slave is a multiple-value component there are two scenarios possible:
Filtering and parsing patterns can be provided via the special rules and techniques described further.
You can provide a pattern, according to which the records in the slave component are either filtered or being populated. It is done via a special rule that is being passed to the bind method as the second parameter.
When you bind two data components, you can provide a pattern according to which preparsed data are filtered in the slave. The pattern can be a string (a record field to filter data by) or a function. The pattern is passed to the bind method as the second parameter.
Filtering slave data by the title field
// input receives value stored in the "title" column
$$("$text1").bind($$("$datatable1"), "title");
Related sample: Binding: Filtering by a Custom Field
If passed as a function the pattern receives the following parameters:
The function compares two values and returns true or false depending on whether the current item should be displayed.
Binding multiple-value components
$$("grid2").bind($$("grid1"), function(slave, master){
// if no record is selected, do not show slave items
if(!master) return false;
// slave datatable displays only records with movie field equal to master record ID
return master.id == slave.movie;
});
Related sample: Data Binding: Filter Linked Table via Binding Rule
Note that in this case the master is a multiple-value component and the master parameter is the whole data record (object). Thus you must specify a field to filter data by (master.id in this case).
Meanwhile, when the master is a single-value component, the master parameter is its value, so filtering is based upon this value:
List data are filtered depending on selected option in Richselect
// here master is an id of the selected option (e.g. 1)
$$("list1").bind($$("richselect1"), function(slave, master){
return slave.category == master;
});
Related sample: Binding: Filter by Binding Rule
Another pattern to display data in the slave is parsing them in it. It assumes that the slave component is initially empty and gets populated upon selection in the master. It also allows you to decide which data should be pushed to the slave. The rule can contain the following flags:
Let’s bind a DataTable to a Tree and push the immediate children of the selected item into the DataTable:
$$("grid1").bind($$("tree1"), "$level");
// where the tree data of the selected node is something like
{ id:"3", value:"Node 3", data:[
// will be shown in slave component
{ id:"3.1", value:"Subnode 3.1" },
{ id:"3.2", value:"Subnode 3.1" }
]}
Related sample: Tree Data Binding
By default, in Tree-like components the children of a data item are introduced by the data key in all the supported data formats. If you want to push data stored by a different key, you can pass its name or provide a function to populate the slave manually as the 3rd parameter of the bind method.
If passed as a string, the source expects the name of the field to get data from. Let's bind a DataTable to a Tree and fill the slave with the data stored in the records field of Tree items:
$$("grid1").bind($$("tree1"), "$data", "records");
// where the tree data of the selected node is something like
{ id:"3", value:"Node 3", records:[
// will be shown in slave component
{ id:"3.1", value:"Subnode 3.1" },
{ id:"3.2", value:"Subnode 3.1" }
]}
Related sample: Tree Data Binding: Subdata
The same technique can be implemented with linear data that contain subdata:
{
view:"list",
id:"list1",
data: [
{value:"1", data:["a", "b"]},
{value:"2", data:["c", "d"]}
],
// ...
}
$$("grid1").bind( $$("list1"), "$data", "data");
Related sample: Data Binding: Linear Data with Subdata
Source can be passed as a function, where you can populate the slave manually. The function takes two parameters:
For example, let's pass a function that will push both data and records from the selected node:
$$("grid2").bind( $$("tree1"), "$data", function(obj, source){
if (!obj) return this.clearAll();
var fulldata = [].concat(source.data.getBranch(obj.id)).concat(obj.records);
this.data.importData(fulldata, true);
});
Related sample: Tree Data Binding: Subdata
Here we get item children with the help of a getBranch method, combine them with data set by the records key and import the resulting array into slave datatable.
You can bind components to view-less data collections.
var dataCollection = new webix.DataCollection({
data: big_film_set
});
$$("form1").bind(dataCollection);
There is one difference from binding to a data component. Binding is based on the selection. Since there is no visual selection in DataCollection, it cannot focus on the item visually. Thus when selecting an item in the visual component synchronized to it you need to set a virtual cursor on the corresponding item in the master DataCollection.
For that call the setCursor() method passing the ID of the selected data record as a parameter:
$$("list1").attachEvent("onAfterSelect", function(id){
data.setCursor(id);
});
Related sample: Data Syncing: Collections and Widgets
The cursor stores the ID of a data item that is currently active in the master component (data collection in this case).
You can also get cursor position with the help of the dedicated getCursor() method:
const cursor = master.getCursor();
The cursor can be deleted to remove the current bind link:
master.setCursor(null);
If a form is bound to the list, and the list is synced with a DataCollection, removing the cursor from the collection will clear the form.
When you bind components, the slave component gets three events:
$$("datatable1").attachEvent("onBindUpdate", function() {
webix.message("Data updated in the master");
const values = $$("form1").getDirtyValues();
$$("textarea1").setValue(JSON.stringify(values, "", "\t"));
});
Related sample: Data Binding: Binding Events
Back to top