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" // ваш скрипт для сохранения
});

Related sample:  Server-side Integration: List

Если для загрузки и сохранения данных используется один и тот же скрипт, укажите свойству save значение true:

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

Инициализация отдельно от компонента

DataProcessor можно инициализировать отдельно от компонента. Это можно сделать с помощью хелпера dp или конструктора DataProcessor.

const dp1 = webix.dp({
    id:"listDP",    // опционально. ID генерируется автоматически
    master:$$("mylist"),
    url:"some_script.php",
    // другие свойства
});
 
const dp2 = new webix.DataProcessor({
    id:"listDP",    // опционально. ID генерируется автоматически
    url:"data.php",
    master:$$("mylist"),
    // другие свойства
});

И dp, и конструктор требуют объект с как минимум двумя полями в качестве параметра:

Смотрите весь список параметров.

Отправка запросов

DataProcessor отслеживает операции на стороне клиента и отправляет POST запрос к серверному скрипту, который указан в свойстве save. Вместе с запросом DataProcessor отправляет название и тип операции:

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

Помимо названия операции, запрос содержит данные об изменённой/добавленной/удалённой записи.

Form Data запроса

id  7
title   The Shawshank Redemption
webix_operation delete

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

Режим Rest

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

  • При вызове метода remove() - создаётся DELETE запрос
  • При редактировании или вызове метода updateItem() - запрос PUT
  • При добавлении данных в компонент - запрос POST.

Чтобы активировать rest прокси, добавьте "rest->" перед путём к вашему срипту:

view: "datatable",
save: "rest->/samples/server/films",

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

Related sample:  Datatable: Data Saving with NodeJS

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

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

  1. функции, которая определяет логику всех типов операций
  2. объект, который определяет логику для каждого типа операций и позволяет использовать события и параметры DataProcessor
  3. прокси

1. 'save' как функция. В этом случае название операции не включено в отправляемые данные, однако передаётся в функцию параметром. Ниже представлены все параметры функции:

  • id - ID записи
  • operation - название операции
  • update - изменённые значения записи.
webix.ui({
    view:"datatable",
    save:function(id, operation, update){
        if (operation === "insert")
            return webix.ajax().post(
                "/server/films",
                update
            );
        /* ...другие операции */
    }
});
 
// или для dataprocessor, инициализированного отдельно
const dp = new webix.DataProcessor({
    master:"datatable1",
    url: function(id, operation, update){
        if (operation === "insert")
            return webix.ajax().post(
                "/server/films",
                update
            );
        /* ...другие операции */
    }
});

2. Вы можете описать логику сохранения отдельно для каждого типа операции. Для этого определите 'save' как объект. Каждое свойство может быть или путём к скрипту, или функцией:

webix.ui({
    view:"datatable",
    save:{
        // функции или строки с URL
        insert: function(id, operation, update){
            return webix.ajax().post(
                "/server/films",
                update
            );
        },
        update: function(id, operation, update){ /* ... */ },
        delete: function(id, operation, update){ /* ... */ }
    }
});

3. Вы также можете указать прокси для сохранения данных. В этом случае вам также необходимо определить save как функцию. Функция принимает следующие параметры:

  • view (object) - экземпляр компонента
  • update (object) - объект изменения
  • dp (object) - экземпляр DataProcessor.

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

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

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

Или для DataProcessor, инициализированного отдельно:

new webix DataProcessor({
    master:"datatable1",
    url: {
        $proxy:true,
        save:function(view, update, dp){ /* ... */ }
    }
});

Related sample:  Datatable: Saving Data with Proxy and Url

Использование API DataProcessor

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

Если вы хотите использовать API DataProcessor при инициализации, вам необходимо определить свойство save как объект с путями/функциями для сохранения в свойстве url и других. Конфигурация DataProcessor выглядит следущим образом:

webix.ui({
    view:"datatable",
    save:{
        url: "some/path", // или функция, объект, прокси
        on:{
            onAfterSave(){ /* */ }
        },
        operationName: "operation",
        /* ...другие свойства конфигурации */
    }
});

Чтобы использовать API динамически, вы можете получить DataProcessor через метод dp, передав в него ID компонента-мастера:

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

Или же передав в метод ID DataProcessor (если вы указывали его при инициализации):

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

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

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

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

  • 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

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

Наверх