Связывание данных

Вы можете связывать данные нескольких компонентов между собой. При выборе какой-либо записи или значения в мастер-компоненте, он передаёт выбранные данные в зависимый компонент. Так как мастер служит источником данных для зависимых компонентов, у него должен быть DataStore или DataValue. В то же время зависимый компонент может хранить как одну запись (например Form или Template), так и множество записей.

Основные принципы

Связывание компонентов

Чтобы связать два компонента, необходимо вызвать метод bind у зависимого компонента, передав мастер в качестве аргумента. Одним из наиболее распространенных примеров связывания, является привязка формы к компоненту данных, например, к List:

$$("form1").bind($$("list1"));

Теперь, если вы выберите любой элемент из списка, форма заполнится его данными. Помните, что свойство name у полей формы должны совпадать с названиями полей данных у мастера:

// компонент данных
{ name:"Jane", age:23 }
// форма
{
    view:"form", id: "form1", elements:[
        { name:"name", view:"text" },
        { name:"age", view:"text", type:"number" }
    ]
}

Сохранение данных в мастер

Изменения в зависимом компоненте также отражаются в мастере. Если вы отредактируете данные элемента списка и сохраните изменения, то они незамедлителдьно отразятся в мастере. Чтобы сохранить изменения, вызовите метод save у формы. Если в момент вызова save поля формы не заполнены данными из мастера, форма добавит новый элемент в мастер (List, в данном случае).

Обратите внимание, что метод save доступен только для привязанных форм:

$$("form1").save();

Related sample:  Creating Basic App: Step 3

Related sample:  Binding to a Native HTML Form

Вместе с явными значениями формы вы можете сохранить и передать дополнительные данные. Для этого необходимо получить объект значений формы с помощью метода getValues и дополнить его желаемыми значениями. После этого, передайте объект методу form.save() в качестве аргумента.

const values = form.getValues();
values.myfield = "My value";
form.save(values);

Отмена привязки данных

Если связь между компонентами вам больше не нужна, вызовите метод unbind у зависимого компонента:

$$("form1").unbind();

Как это работает

Связывание данных - синтаксический сахар для более прямолинейного подхода к соединению компонентов посредством событий. В частности, приведенный выше пример привязки формы к списку можно переделать с помощью следующего API:

// выбираем элемент из списка и заполняем форму
$$("list1").attachEvent("onAfterSelect", function(id){
    var item = this.getItem(id);
    $$("form1").setValues(item);
});
 
// сохраняем данные в списке
view:"form", id:"form1", elements:[
    // ...
    {
        view:"button", value:"Save", click:function(){
            var item = this.getFormView().getValues();
            $$("list1").updateItem(item.id, item);
        }
    }
]

Такой подход пригодится, если вы работаете с Webix Jet. Дело в том, что форма и ее мастер скорее всего будут находиться в разных файлах и связать их с помощью метода bind не получится.

Привязка двух форм к одному компоненту

Привязка двух форм к одному и тому же компоненту - нестандартная задача, однако вполне реализуемая. По умолчанию, если вы привяжете две формы к одному мастеру и попытаетесь сохранить данные каждой из них, данные из первой формы будут заменены данными из второй формы.

Существуют два решения этой проблемы:

  • получить значения двух форм через метод getDirtyValues, который возвращает только обновленные данные, и объединить их с помощью метода extend:
const v1 = $$("form1").getDirtyValues();
const v2 = $$("form2").getDirtyValues();
$$("form1").save(webix.extend(v1, v2, true) ); // комбинируем значения двух форм
  • использовать метод saveBatch, который одновременно сохраняет значения нескольких форм, вместе с методом getDirtyValues:
$$("datatable1").saveBatch(function(){
    $$("form1").save();
    $$("form2").save($$("form2").getDirtyValues());
});

Related sample:  Binding Two Forms to the Same Master

Метод getDirtyValues возвращает только измененные значения формы.

Предотвращение undefined значений

В этом случае речь только о тех зависимых компонентах, у которых в конфигурации определено свойство template. Компоненты Form и html Form привязываются согласно свойствам name.

Когда ни одно из значений мастера не выбрано, зависимый компонент отображает значение undefined. Однако вы можете указать значения по умолчанию. Для этого, в мастере нужно определить свойство defaultData:

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

Связывание и получение данных с сервера

Как правило, зависимый компонент не взаимодействует с сервером напрямую. Он получает данные из мастера, модифицирует их, и возвращает обратно. После чего уже мастер отправляет данные на сервер. Однако, зависимый компонент может самостоятельно загружать данные с сервера. Данные подгружаются согласно выбранным записям/значениям в мастере. Для этого предусмотрено свойство dataFeed.

webix.ui({
  view:"form",
  id:"form1",
  // ... другие конфигурации
  dataFeed: "slave_data.php"
});
 
$$("form1").bind("datatable1")

Related sample:  Datatable: Binding with Form

Свойство dataFeed хранит URL, по которому зависимый компонент будет отправлять запрос, когда пользователь выберет какой-либо элемент в мастере.

Этот принцип одинаково работает как для зависимых форм, так и для коллекций (компонентов данных), однако параметры URL в обоих случаях отличаются:

  • для форм: "action=get&id="+obj.id
  • для компонентов данных: "filter[id]="+obj.id

где obj это данные выбранного в мастере элемента.

Мастер с одним значением

В качестве мастера могут выступать компоненты, которые хранят только одно значение (Richselect, Datepicker и другие). Когда значение меняется, оно автоматически передается зависимому компоненту.

$$("template1").bind($$("calendar1"));

Related sample:   Data Binding: Template Bound to Calendar

Зависимый компонент со множественными значениями

Этот раздел рассказывает о зависимых компонентах, у которых есть DataStore или TreeStore (DataTable, Tree, List, Chart и другие).

Когда зависимый компонент хранит много значений, возможны 2 сценария развития:

  • данные предварительно парсятся в зависимый компонент и фильтруются при выборе записи в мастере
  • у зависимого компонента изначально нет данных. Данные парсятся в компонент при выборе записи в мастере.

Шаблоны фильтрации и парсинга данных указываются при помощи специальных правил и техник, описанных ниже.

Правила для шаблона привязки

Вы можете задать шаблон фильтрации или парсинга данных для зависимого компонента. Делается это с помощью специального правила, которое передается методу bind в качестве второго параметра.

Фильтрация данных зависимого компонента

При связывании компонентов, вы можете указать специальный шаблон, согласно которому данные зависимого компонента будут фильтроваться, или парситься (если компонент изначально пуст). Шаблон может быть строкой (полем, по которому необходимо фильтровать данные) или функцией. Он передается методу bind в качестве второго параметра.

Фильтрация данных зависимого компонента по полю title

// поле получает значение из столбца “title”
$$("$text1").bind($$("$datatable1"), "title");

Related sample:  Binding: Filtering by a Custom Field

Шаблон в качестве функции получает следующие параметры:

  • slave - объект данных зависимого компонента
  • master - выбранная запись в главном компоненте (объект данных или значение в зависимости от типа компонента).

Функция сравнивает два значения и возвращает true или false в зависимости от того, должен ли отображаться текущий элемент.

Связывание компонентов с множественными значениями

$$("grid2").bind($$("grid1"), function(slave, master){
// если запись в мастере не выбрана, зависимый компонент останется пустым
if(!master) return false;
/* зависимый компонент *Datatable* отобразит только те записи, 
значения "movie" которого будут соответствовать значениям ID мастера */
    return master.id == slave.movie;
});

Related sample:  Data Binding: Filter Linked Table via Binding Rule

Обратите внимание что, если мастер - это компонент со множественными значениями, то параметр master хранит все его значения (объект данных). В этом случае необходимо указать поле для фильтрации данных (здесь master.id ).

Если же мастер - это компонент с единственным значением, то параметр master хранит его значение. Фильтрация данных происходит по этому значению:

Данные компонента *List* фильтруются в зависимости от выбранной опции в *Richselect*

// здесь master это id выбранной опции (например 1)
$$("list1").bind($$("richselect1"), function(slave, master){
  return slave.category == master;
});

Related sample:  Binding: Filter by Binding Rule

Парсинг данных в зависимый компонент

Другой вариант для отображения данных в зависимом компоненте - парсинг данных. Предполагается, что зависимый компонент изначально пустой и заполняется при выборе элемента в мастере. Такой подход позволяет вам решать, какие данные следует передать зависимому. У правила могут быть следующие значения:

  • $level - парсит только дочерние элементы первого уровня у выбранного элемента (только для иерархических данных)
  • $data - парсит данные с указанного поля (поле указывается через параметр source метода bind).

Давайте свяжем DataTable с Tree и заполним DataTable дочерними элементами первого уровня:

$$("grid1").bind($$("tree1"), "$level");
 
// древовидные данные выбранного элемента выглядят следующим образом
{ id:"3", value:"Node 3", data:[
  // заполнят зависимый компонент
    { id:"3.1", value:"Subnode 3.1" },
    { id:"3.2", value:"Subnode 3.1" }
]}

Related sample:  Tree Data Binding

Произвольное поле для парсинга в зависимый компонент

По умолчанию, данные дочерних элементов древовидных компонентов представлены ключом data во всех поддерживаемых форматах данных.

Если вы хотите заполнить компонент данными, которые хранятся под другим ключом, передайте имя ключа или функцию (для парсинга данных вручную) в метод bind в качестве третьего параметра.

Source в виде строки

source в качестве должен соответствовать названию поля из которого будут браться данные. Давайте свяжем DataTable с Tree и запарсим в него данные, которые хранятся в полях records у элементов Tree:

$$("grid1").bind($$("tree1"), "$data", "records");
 
// данные выбранного элемента выглядят следующим образом:
{ id:"3", value:"Node 3", records:[
    // парсятся в зависимый компонент
    { id:"3.1", value:"Subnode 3.1" },
    { id:"3.2", value:"Subnode 3.1" }
]}

Related sample:  Tree Data Binding: 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 в виде функции

source можно передать в качестве функции, с помощью которой можно запарсить данные в зависимый компонент вручную. Функция принимает два параметра:

  • obj - объект данных выбранного элемента в мастере
  • source - сам мастер.

Давайте определим функцию, которая будет парсить и данные, и записи выбранного элемента:

$$("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

Здесь мы получаем дочерние элементы с помощью метода getBranch, комбинируем их с набором данных под ключом records и импортируем полученный массив в зависимый компонент Datatable.

Связывание с коллекциями данных

Вы можете связывать компоненты с хранилищами данных.

var dataCollection = new webix.DataCollection({
  data: big_film_set
});
 
$$("form1").bind(dataCollection);

Есть одно отличие между привязкой к компоненту и привязкой к коллекции. Так как связывание данных основано на выборе элементов в мастере, а у DataCollection нет визуального представления, то и выбрать элемент в коллекции привычным способом не получится. Поэтому при выборе элемента в видимом компоненте, который синхронизирован с коллекцией необходимо поместить "виртуальный" курсор на соответствующий элемент мастер-коллекции.

Для этого нужно вызвать метод setCursor(), передав ID выбранного элемента в качестве параметра:

$$("list1").attachEvent("onAfterSelect", function(id){
  data.setCursor(id);
});

Related sample:  Data Syncing: Collections and Widgets

Курсор хранит ID элемента данных, который выбран в мастере (в данном случае это коллекция данных).

Вы также можете получить текущее положение курсора с помощью метода getCursor():

const cursor = master.getCursor();

При удалении курсора, связывание данных с коллекцией отменяется:

master.setCursor(null);

Если форма связана со списком, а список синхронизован с DataCollection, удаление курсора из коллекции приведет к очищению формы.

События связывания данных

Когда вы связываете компоненты, зависимый компонент получает 3 события:

  • onBindApply – срабатывает в момент вызова метода bind
  • onBindRequest - срабатывает когда зависимый компонент готов принять данные из мастера
  • onBindUpdate – срабатывает, когда значение в зависимом компоненте изменяется и вызывается метод save() для обновления мастера.
$$("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

Наверх