Intermediate

DataProcessor

Webix DataProcessor - это инструмент, который позволяет "общаться" с сервером. Основной функционал DataProcessor:

  • работает на стороне клиента как миксин библиотеки;
  • отслеживает события компонента: добавление, обновление, удаление и перемещение данных. Передаёт серверу POST-запрос с изменёнными данными, а также названием операции (insert, update, delete).
  • умеет валидировать данные перед отправкой их серверному скрипту;
  • работает с любыми компонентами данных и DataCollections.

Если не использовать DataProcessor, то для подготовки данных к дальнейшему использованию вам необходимо будет добавить соответствующие функции в события компонента (onAfterInsert/Update/Delete).

Чтобы посмотреть полный список методов, свойств и событий DataProcessor, перейдите в раздел API.

Инициализация DataProcessor

Вы можете инициализировать DataProcessor явно и неявно.

Неявная инициализация с помощью свойства Save

DataProcessor автоматически инициализируется, когда вы определяете свойство save у компонента-мастера, как путь к скрипту для сохранения данных:

webix.ui({
    view:"datatable",
    autoConfig:true,
    url:"data_load.php", // ваш скрипт для загрузки
    save:"data_save.php" // ваш скрипт для сохранения
});

Явная инициализация

Явно инициализировать DataProcessor можно в краткой и полной формах. Обязательные параметры:

Ознакомьтесь с полным списком параметров.

Краткая форма

Краткая форма возвращает DataProcessor компонента. Его процессора нет - создаёт новый.

Краткая форма (master, url)

webix.dp({
    id:"listDP",    // необязательно, ID может быть сгенерировано автоматически
    master:$$("mylist"),
    url:"some_script.php",
    // другие свойства
});

Полная форма

Используется при создании нового DataProcessor с помощью конструктора webix.DataProcessor():

Полная форма

var dp = new webix.DataProcessor({
    id:"listDP",    // необязательно, ID может быть сгенерировано автоматически
    url:"data.php",
    master:$$("mylist"),
    // другие свойства
});

Related sample:  Server-side Integration: List

Как получить объект DataProcessor

У DataProcessor есть набор методов и свойств для изменения шаблона обработки данных по умолчанию. Вы можете получить доступ к ним через метод dp.

Вы также можете получить DataProcessor через ID компонента-мастера:

var dp = webix.dp($$("mylist")); // возвращает объект процессора для компонента "list"

Или же через ID самого DataProcessor, если вы создавали его явно:

var dp = webix.dp($$("mydp"));

И наоборот, компонент-мастер доступен через объект конфигурации, внутри обработчиков событий DataProcessor:

dp.attachEvent("onSomeEvent", function(id, status, obj){
   var grid = this.config.master; // this == DataProcessor
});

Операции обработки данных

DataProcessor интерпретирует операции клиента над данными и определяет их тип. Набор методов и соответствующих им операций:

  • При вызове метода remove() - операция "delete"
  • При редактировании или вызове метода updateItem() - операция "update";
  • При добавлении данных в компонент - операция "insert".

Название операции, а также данные изменённой/добавленной/удалённой записи отправляются серверному скрипту POST запросом в момент любого изменения данных.

Данные запроса

id  7
title   The Shawshank Redemption
webix_operation delete

С помощью параметра operationName вы можете указать другое имя для операции.

Режим Rest

Webix также умеет работать с RESTful запросами через прокси. В этом случае методы и операции следующие:

  • При вызове метода remove() - создаётся DELETE запрос;
  • При редактировании или вызове метода updateItem() - запрос PUT;
  • При добавлении данных в компонент - запрос POST;
view:"datatable",
save: "rest->/samples/server/films",

В режиме REST, Webix не включает "webix_operation" в запрос, т.к. тип операции определяется типом запроса.

Данные запроса

id  7
title   The Shawshank Redemption

Related sample:  Datatable: Data Saving with NodeJS

Пользовательский шаблон для сохранения

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

Вы можете инициализировать DataProcessor с функцией сохранения, которая принимает ID записи, название операции и изменённую запись в качестве параметров:

webix.ui({
    view:"datatable",
    save:function(id, operation, update){ /* ... */ }
});
 
// или явно
new webix.DataProcessor({
    master:"datatable1",
    url: function(id, operation, update){ /* ... */ }
});

Или вы можете описать прокси для сохранения. Функция для сохранения принимает экземпляр компонента, объект изменений и экземпляр DataProcessor в качестве параметров:

webix.ui({
    view:"datatable",
    save:{
        $proxy:true,
        save:function(view, params, dp){ /* ... */ }
    }
});
 
// или явно
new webix DataProcessor({
    master:"datatable1",
    url: {
        $proxy:true,
        save:function(view, update, dp){ /* ... */ }
    }
});

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

Функция для сохранения

view:"datatable",
save:function(id, operation, update){
    if (operation == "insert")
        return webix.ajax().post("/samples/server/films", update);
    // ... другие свойства
}

В случае прокси для сохранения название операции доступно в объекте изменений:

Прокси для сохранения

view:"datatable",
save:{
    $proxy:true,
    save:function(view, update, dp){
        var id = update.data.id;
        if (params.operation == "insert")
            return webix.ajax().post("/samples/server/films", params.data);
        // ... другие свойства
    }
}

Related sample:  Datatable: Saving Data with Proxy and Url

Ответ с сервера

В случае успеха

В случае успешного сохранения данных сервер должен вернуть данные со следующими параметрами:

  • status - статус операции ("success"/"error"/"invalid"). Может быть опущен для успешных операций;
  • id - ID элемента, который отправляется на сервер;
  • newid - ID элемента, который вернул сервер после обновления. Обычно это происходит при добавлении новых записей.
  • другие изменения, которые можно применить к записи, если включён updateFromResponse.

Параметр ID является обязательным только для новых записей. Запись добавляется в UI компонент, где получает временное ID. На сервере же у этой записи другое ID, предоставляемое базой данных.

Если вы вернёте этот серверный ID как параметр ответа, DataProcessor автоматически заменит клиентский ID на серверный. Обратите внимание, что поле id можно возвращать только как новый ID, или же как id и newid для старого и нового ID соответственно.

В случае ошибки

В случае ошибки при сохранении данных, ответ с сервера должен содержать следующее:

  • status - статус операции ("error" или "invalid");
  • id - ID элемента, который отправляется на сервер.

Обратите внимание, что пустой ответ расценивается как ошибка и вызовет соответствующие события с полем status:"error".

Обработка событий описана ниже.

Обновление данных через сервер

Как было заявлено ранее, new ID автоматически замещает временное клиентское ID. Это поведение DataProcessor по умолчанию, поэтому дополнительных действий не требуется.

Кроме этого, вы можете обновлять данные всех полей, которые участвуют в операциях insert и update. Что нужно добавить в код:

  • настройте ответ с сервера так, чтобы он возвращал весь объект данных (а не только статусы и ID);
  • задайте свойству DataProcessor updateFromResponse значение true:

При явной инициализации DataProcessor

new webix.DataProcessor({
    updateFromResponse:true,
    master:"datatable1",
    url:"..."
});

При неявной инициализации DataProcessor

view:"datatable",
save:{
    url:"...",
    updateFromResponse:true
}

Related sample:  Datatable: Updating

Это может быть полезно в случае REST-full приложений или же когда вам необходимо заполнить клиентские поля, значения которых можно получить только с сервера.

События DataProcessor

События DataProcessor позволяют отслеживать изменения данных на клиенте. Что можно делать:

1) Изменять данные перед отправкой на сервер с помощью события onBeforeDataSend, которое принимает весь объект данных в качестве параметра:

dp.attachEvent('onBeforeDataSend', function(obj){
    obj.data.StartDate = webix.i18n.dateFormatStr(obj.data.StartDate);
});

Или добавлять обработчики для конкретных операций, например с помощью события onBeforeDelete:

webix.dp($$("grid")).attachEvent("onBeforeDelete", function(id, action){
    action.operation = "update";
    action.data.deleted = webix.i18n.parseFormatStr(new Date());
});

2) Отслеживать ответы в случае успеха с помощью метода onAfterSync:

Успешный ответ с сервера

dp.attachEvent('onAfterSync', function(statusObj, text, data, loader){
    //statusObj {status:"success", id:"12"}
 
    var hash = data.json().data;
    //hash { id:"12"}
});

Посмотрите на данные ответа, чтобы получить более детальную информацию.

3) Отслеживать ответы в случае ошибки с помощью события onAfterSaveError:

Ответ в случае ошибки

dp.attachEvent('onAfterSaveError', function(id, status, response, details){
    // id - record id
    // status - response status {id:"1", status:"error"}
});

В случае ошибки при сохранении, ответ с сервера должен содержать статус "error" или "invalid".

Как отследить момент сохранения

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

Например, вот так вы можете добавить данные в компонент и отследить результат. Если операция одна, промис возвращает объект, который пришёл с сервера:

$$("grid").waitSave(function(){
    this.add({
        rank:99, title:"", year:"2012", votes:"100"
    });
}).then(function(obj){
    // сервер возвращает объект данных с серверным ID
    $$("grid").select(obj.id);
});

Если же вы хотите отследить результат нескольких операций сохранения, промис вернёт массив объектов:

$$("grid").waitSave(function(){
    for (var i = 0; i < 3; i++){
        this.add({
            rank:99, title:"", year:"2012", votes:"100"
        });
    }
}).then(function(arr){
    for (var i = 0; i < arr.length; i++){
        $$("grid").select(arr[i].id, i);
    }
});

Related sample:  Datatable: Wait Saving

Блокировка и ограничение автосохранений

Если вы не желаете сразу сохранять все изменения на сервер, вы можете временно отключить DataProcessor. Существует несколько способов:

  • Применить метод the ignore к объекту DataProcessor
webix.dp($$("grid")).ignore(function(){
    $$("grid").add(data);
});
  • Выключить DataProcessor перед обновлением данных методом off
dp.off();
$$("grid").add(data);
dp.on();
  • Заблокировать автосохранение данных с помощью свойства autoupdate и отправить изменения вручную.
// изначально при инициализации dp 
new webix.DataProcessor({
    master:"datatable1",
    url:"...",
    autoupdate:false
});
 
// динамически
webix.dp($$("datatable1")).define("autoupdate", false);

Чтобы отправить конкретные изменённые данные, используйте метод save с ID элемента и названием операции в качестве параметров:

webix.dp($$("datatable1")).save(1, "update");

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

DataProcessor при драг-н-дроп и операциях перемещения

Вы можете отслеживать Д-н-Д операции и перемещение элементов, а также другие CRUD операции при активном свойстве trackMove.

new webix.DataProcessor({
    master: tree,
    url: "...",
    trackMove:true
});

Related sample:  Server-side Integration: Tree

DataProcessor и валидация данных

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

dp = new webix.DataProcessor({
    rules:{
        $all:webix.rules.isNotEmpty
    },
    url: "save.php",
    master: $$("mylist")
});

Отправка заголовков в запросах DataProcessor

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

1. Если DataProcessor отправляет запросы самостоятельно, вы можете отслеживать событие onBeforeAjax, чтобы изменять любой AJAX-запрос со страницы:

webix.attachEvent("onBeforeAjax",
    function(mode, url, data, request, headers, files, promise){
        headers["Content-type"] = "application/json";
    }
);

2. Если же вы используете функцию save или прокси для сохранения, вы можете добавить заголовки с помощью метода webix.ajax().header().

Related sample:  Saving: Proxy and Url

"Пессимистичное" сохранение данных

По умолчанию все изменения данных сначала применяются на клиенте и только после этого отправляются на сервер. Вы можете отправить данные на сервер в любой момент с помощью метода save. Метод ожидает следующие параметры:

  • id - ID изменённого элемента;
  • operation - тип операции (например, "update", "insert", "delete");
  • data - объект данных, который отправляется на сервер.

save() возвращает промис с объектом ответа сервера. Промис можно использовать при обновлении клиентских данных. Обратите внимание, чтобы избежать ещё одного ненужного сохранения данных, вам необходимо обернуть клиентскую операцию в ignore.

webix.dp($$("list")).save(
    webix.uid(),
    "insert",
    { name:"New User", email:"", roles:"" }
).then(function(obj){
    webix.dp($$("list")).ignore(function(){
        $$("list").add(obj);
    });
});

Чтобы отследить неудачное сохранение данных, добавьте ещё один обработчик в then():

webix.dp($$("list")).save(
    webix.uid(),
    "insert",
    { name:"New User", email:"", roles:"" }
).then(function(obj){
    webix.dp($$("list")).ignore(function(){
        $$("list").add(obj);
    });
}, function(){
    webix.message("Data were not saved");
});

Related sample:  Datatable: Pessimistic Data Saving

DataProcessor без компонента-мастера

Вы можете инициализировать DataProcessor без компонента-мастера:

var serverData = new webix.DataProcessor({ url:"/server/patients" });

Преимущества такого подхода:

  • DataProcessor без привязанного компонента можно использовать как серверное API сразу для нескольких компонентов.

Код "пессимистичного" сохранения данных в этом случае будет короче:

serverData.save(
    webix.uid(),
    "insert",
    { name:"New User", email:"", roles:"" }
).then(function(obj){
    $$("list").add(obj);
});

Related sample:  Datatable: Pessimistic Data Saving

Статьи по теме

Наверх