Webix DataProcessor is a functional library that lets you "communicate" with a server-side backend. DataProcessor:
Without DataProcessor, you need to attach corresponding functions to component events (onAfterInsert/Update/Delete) to get data ready for processing.
You can find the full list of DataProcessor methods, properties and events in the API Reference.
DataProcessor can be initialized implicitly and explicitly.
When you define the save property of a data component (master) as the path to the necessary script for saving data, DataProcessor is automatically initialized:
webix.ui({
view:"datatable",
autoConfig:true,
url:"data_load.php", // your custom script for loading
save:"data_save.php" // your custom script for saving
});
DataProcessor can be initialized explicitly in the long and short forms. The compulsory parameters include:
Go to the complete list of other parameters.
The short form returns the DataProcessor of a component. If there isn't any, it creates a new DataProcessor.
Short Form (master, url)
webix.dp({
id:"listDP", // optional, the ID can be auto-generated
master:$$("mylist"),
url:"some_script.php",
//other properties
});
The long form is used to create a new DataProcessor. In this case, DataProcessor is created with the webix.DataProcessor() constructor:
Full Form
var dp = new webix.DataProcessor({
id:"listDP", // optional, the ID can be auto-generated
url:"data.php",
master:$$("mylist"),
//other properties
});
Related sample: Server-side Integration: List
DataProcessor has a set of methods and properties for changing the default processing pattern. You can use them by accessing the DataProcessor with the Webix dp method.
You can get the DataProcessor either by the ID of the master component:
var dp = webix.dp($$("mylist")); // returns dataprocessor object for "list" view
Or by the ID of the DataProcessor, if you have set it explicitly:
var dp = webix.dp($$("mydp"));
Vice versa, inside DataProcessor event handlers, you can reach the master component through the DataProcessor configuration object:
dp.attachEvent("onSomeEvent", function(id, status, obj){
var grid = this.config.master; // this == DataProcessor
});
DataProcessor interprets client-side operations performed over a data item and defines the type of the operation for it. The default processing looks as follows:
This operation name alongside with the data of the edited/added/deleted record are sent to the server script by a POST request the moment any change is performed.
Request data
id 7
title The Shawshank Redemption
webix_operation delete
You can define another operation name with the help of the operationName setting of DataProcessor.
Webix can also trigger RESTful server-side requests via its rest proxy. It this case, the processing looks as follows:
view:"datatable",
save: "rest->/samples/server/films",
In REST mode, Webix does not append "webix_operation" to request data, since the operation type is stated by the request type.
Request data
id 7
title The Shawshank Redemption
Related sample: Datatable: Data Saving with NodeJS
You can provide custom logic for save operations triggered by DataProcessor. In this way, you benefit from automatic data saving and the ability to modify the requests sent to server.
You can initialize a DataProcessor with a saving function that receives the record ID, the operation name, and the changed record as parameters:
webix.ui({
view:"datatable",
save:function(id, operation, update){ /* ... */ }
});
//or, explicitly
new webix DataProcessor({
master:"datatable1",
url: function(id, operation, update){ /* ... */ }
});
Or, you can provide a data proxy for saving. The save function receives component instance, update object and DataProcessor instance as parameters:
webix.ui({
view:"datatable",
save:{
$proxy:true,
save:function(view, params, dp){ /* ... */ }
}
});
//or, explicitly
new webix DataProcessor({
master:"datatable1",
url: {
$proxy:true,
save:function(view, update, dp){ /* ... */ }
}
});
For such custom solutions, the operation name is not appended to the sent data. Instead, you should get it as a proxy or a function parameter and decide how to inform the server about the client-side changes:
Save function
view:"datatable",
save:function(id, operation, update){
if (operation == "insert")
return webix.ajax().post("/samples/server/films", update);
// ... other operations
}
With the save proxy the operation name is available as a part of the update object:
Save proxy
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);
// ... other operations
}
}
Related sample: Datatable: Saving Data with Proxy and Url
To indicate successful data saving, the server side should return a non-empty response with the following parameters:
IDs are necessary for the inserted records only. During adding, the record is added to the UI component where it receives the temporary client-side ID, while on the server it receives another ID provided by the database.
If you return this server-side ID as a response parameter, DataProcessor will automatically change the client-side ID for the correct server-side ID. Note that you can return only the id field as the new ID, or both id and newid for old and new IDs respectively.
To indicate data saving error, the server side should return a response containing:
Note that en empty response is also considered an error and will trigger error events with the status:"error" field.
Event handling is described below.
As it was stated earlier, the new ID from response of an insert request will automatically replace the temporary client-side ID. This is part of default DataProcessor behavior, and no specific actions are required.
Additionally, you can enable data update for all fields taking part in insert and update operations. It requires the following additions to the code:
Either during explicit DataProcessor definition
new webix.DataProcessor({
updateFromResponse:true,
master:"datatable1",
url:"..."
});
Or when defining DataProcessor implicitly
view:"datatable",
save:{
url:"...",
updateFromResponse:true
}
Related sample: Datatable: Updating
It can be useful for REST-full applications or when you need to fill in client-side fields which values can be calculated only on server side.
The event system for DataProcessor helps track different changes of data saving right on the client side. Here you can:
1) Modify data before it's gone to server with the onBeforeDataSend event that receives the whole data object as a parameter:
dp.attachEvent('onBeforeDataSend', function(obj){
obj.data.StartDate = webix.i18n.dateFormatStr(obj.data.StartDate);
});
Or attach operation-specific handlers, e.g. onBeforeDelete:
webix.dp($$("grid")).attachEvent("onBeforeDelete", function(id, action){
action.operation = "update";
action.data.deleted = webix.i18n.parseFormatStr(new Date());
});
2) Track successful server-side responses with the help of onAfterSync event:
Successful server response
dp.attachEvent('onAfterSync', function(statusObj, text, data, loader){
//statusObj {status:"success", id:"12"}
var hash = data.json().data;
//hash { id:"12"}
});
Check response data for more details.
3) Track unsuccessful server-side responses with onAfterSaveError event:
Unsuccessful server response
dp.attachEvent('onAfterSaveError', function(id, status, response, details){
// id - record id
// status - response status {id:"1", status:"error"}
});
To indicate an error that happened during saving, the response data should contain "error" or "invalid" status.
You can also catch the moment when the data changes are saved to the server with the help of the waitSave method of data components. waitSave expects a function with one or several data operations and returns a promise that resolves when data saving succeeds and rejects when it fails.
For example, this is how you can add data into the component and catch the result. When there is one data operation, the promise is resolved with an object that is returned from the server:
$$("grid").waitSave(function(){
this.add({
rank:99, title:"", year:"2012", votes:"100"
});
}).then(function(obj){
// server returns a data object with the server ID
$$("grid").select(obj.id);
});
If you want to catch the result of a several-operation save, the promise will resolve with an array of objects:
$$("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
If you do not want to save all updates to the server, you can temporarily cancel DataProcessor. There are several options for doing this:
webix.dp($$("grid")).ignore(function(){
$$("grid").add(data);
});
dp.off();
$$("grid").add(data);
dp.on();
//initially during dp configuration
new webix.DataProcessor({
master:"datatable1",
url:"...",
autoupdate:false
});
//dynamically
webix.dp($$("datatable1")).define("autoupdate", false);
To trigger sending of a specific data update, use the save method with the item ID and the operation name as parameters:
webix.dp($$("datatable1")).save(1, "update");
You can also save all changes at once - read more in Webix Proxy Objects.
DnD operations and data item moving can be tracked by DataProcessor like any other CRUD operation provided that you switch on DataProcessor trackMove functionality.
new webix.DataProcessor({
master: tree,
url: "...",
trackMove:true
});
Related sample: Server-side Integration: Tree
Data validation is enabled by including specific rules for input field. You can read about them here. With rules specified, the validation process starts each time you try to save data to the database.
dp = new webix.DataProcessor({
rules:{
$all:webix.rules.isNotEmpty
},
url: "save.php",
master: $$("mylist")
});
You cannot send headers with DataProcessor requests as they are executed in the background. There are other ways you can do this:
1. If DataProcessor sends requests on its own, you can catch Webix onBeforeAjax request to modify any Ajax request issued from the page:
webix.attachEvent("onBeforeAjax",
function(mode, url, data, request, headers, files, promise){
headers["Content-type"] = "application/json";
}
);
2. If you use the save function or a save proxy, you can add headers there with the webix.ajax().header() method.
Related sample: Saving: Proxy and Url
By default, all data changes are first applied on the client side and only after that are sent to the server. You can explicitly send data to the server at any moment with the help of the save method. save() expects the following parameters:
save() returns a promise that resolves with the server response object and can be used to update the client side data with the server response data. Note that to avoid one more data saving, you need to wrap the client-side data operation in ignore.
webix.dp($$("list")).save(
webix.uid(),
"insert",
{ name:"New User", email:"", roles:"" }
).then(function(obj){
webix.dp($$("list")).ignore(function(){
$$("list").add(obj);
});
});
To catch an unsuccessful result of data saving, add one more handler function to 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 can also be initialized without a master component:
var serverData = new webix.DataProcessor({ url:"/server/patients" });
The benefits of this approach are these:
Pessimistic data saving with a standalone dataprocessor will be shorter in code:
serverData.save(
webix.uid(),
"insert",
{ name:"New User", email:"", roles:"" }
).then(function(obj){
$$("list").add(obj);
});
Related sample: Datatable: Pessimistic Data Saving