Tuning Webix for Odata

As we know, web app development is a complex process that, on the one hand, consists of laborious work at the backend and, on the other hand, includes plenty of efforts while creating a nice-looking and responsive UI.

If you want to optimize the construction of your web applications, you need to find the best option for organizing your work with data as well as a fast and powerful js library for drawing app interface.

This variant can be implemented by using OData, an open data protocol for building RESTful API’s, and by adapting Webix UI library to its rules of requesting and modifying data.

Odata protocol

OData’s goal is to enable a broad access to data regardless of the way it is stored. It allows requesting and updating resources via HTTP commands and provides an entire query language directly in the URL.

Well, if you are reading this post, you probably know a lot about Webix. If suddenly no, you can find more info here. All in all, I can say that our users find it fast and easy.

twitter review of webix

There is a wide range of services that expose their data using the OData protocol. Among them are such popular ones as Windows Azure Table Storage, Microsoft Dynamics, SharePoint, IBM DB2. You can find the full list of them here.

To show you how to build a feature-rich web app with a flexible backend and stylish user interface, we use a recently released DataBoom service. DataBoom is a DBAAS solution with a powerful rest API based on OData standard.

DataBoom database

We’ve prepared some samples built with Webix and DataBoom. To check them please continue reading this post.

Creating a Proxy

Standard loading and saving can be enabled by this codeline:

webix.ui({ view:"datatable", url:some_url, save:some_url });

To add custom logic, we need to create a proxy object that will organize client-server communication according to the desired pattern. Let’s give a speaking name to our proxy and fetch the data from “persons” collection at the server-side:

webix.proxy.odata = {
    $proxy:true,
    load:function(view, callback, url){ ..custom loading pattern..},
    save:function(view, callback, url){ ..custom saving pattern..}
};

webix.ui({
      view:"datatable",
      url:"odata->https://t529.databoom.space/api1/b529/collections/persons",
      save: "odata->https://t529.databoom.space/api1/b529/collections/persons"
});

DataBoom is an online database that allows storing and retrieving data with a few lines of code without database administration and schema definition. You can create a free account and use an online database for working with OData.

Data Loading

To comply with Odata requirements of data requesting, we need to configure the query string each time we load, filter or sort the data:

load:function(view, callback, details){
    var url = this.source; //https://t529.databoom.space/api1/b529/collections/persons
    url += "$format=json"; //ensures that data is returned in JSON format

    // if server-side sorting or filtering is triggered
    if(details){
      var start = details.from, count = details.count;

      if(details.sort)
          url += "&$orderby="+details.sort.id+" "+details.sort.dir;

      if(details.filter){
        var filters = [];
        for (var key in details.filter){
          if(details.filter[key])
            filters.push("startswith("+key+",'"+(details.filter[key]?details.filter[key]:"")+"')");
        }
       if(filters.length) url +="&$filter="+filters.join(" and ");
             }
    }
    //GET request
    webix.ajax(url).then(function(data){
        data = data.json();
        var records = data.d.results;
        webix.ajax.$callback(view, callback, "", records, -1);
    });
}

Data Saving

To insert, update or delete a record in Odata way we need to send a request of an appropriate type. Odata conventions require sending a POST for creating a new record, PUT or PATCH for updating it and DELETE for deleting a record.

saving data with odata and webix

We also need to set a request header that specifies the type of data that goes to server. It should be “application/json”.

save:function(view, update, dp, callback){
    var url = this.source,
        mode = update.operation,
        data = update.data,
        editLink = url.replace(/\/$/, "")+"("+data["id"]+")";

    if(mode == "insert") delete data.id;
    data = JSON.stringify(data);

    //call odata URI
    if(mode == "insert"){
        webix.ajax().headers({
            "Content-type":"application/json"
        }).post(url, data, callback);
    } else if (editLink){
        if (mode == "update"){
            webix.ajax().headers({
                "Content-type":"application/json"
            }).put(editLink, data, callback);
        } else if (mode == "delete") {
            webix.ajax().headers({
                "Content-type":"application/json"
            }).del(editLink, data, callback);
        }
    }
}

After the data saving request is fulfilled, the response should be processed in order to replace an auto-generated client-side ID to the real server-side ID:

result:function(state, view, dp, text, data, loader){
    data = data.json();
    var obj = state;
    if (data){
        obj = data.d.results || {status:"error"};
        obj.newid = data.d.results.id;
        dp.processResult(state, obj);
    }
}

Check the sample by this link.

Dynamic Loading with Odata

To enable dynamic loading within a data-management component, Webix requires a definite format of the incoming data so that later on this component can request a certain amount of data at a certain position:

{ data:[..records..], pos:0, total_count:1000 }

So the above proxy should be supplied with additional logic to fetch total data count and starting position from server. Also, a query string should be modified each time a request for a new portion of data is prepared:

load:function(view, callback, details){
    if(this.dynamicLoading){
        url += "&$inlinecount=allpages";
        if(!details) url+="&$top="+view.config.datafetch;
    }

    if(details){
        var start = details.from, count = details.count;
        if(start && count!==-1){
            if(start<0) start = 0;
            url += "&$skip="+start+"&$top="+count;
         }
    }
 }

webix.ajax(url).then(function(data){
    data = data.json();
    if(data.d){
        if(data.d.__count){
            var records = {
                data:data.d.results,
                pos:data.d.__skip||0,
                total_count:data.d.__count*1
            };
        }
        else
            var records = data.d.results;
        webix.ajax.$callback(view, callback, "", records, -1);
    }
});
}

And the proxy for dynamic loading will look like:

webix.proxy.odataDynamic = {
    init:function(){
        webix.extend(this, webix.proxy.odata);
    },
    dynamicLoading:true
};

Evaluate the sample of dynamic loading with OData.

Conclusion

Webix way of loading and saving data can be seamlessly customized to adjust to different data services and protocols. The library package already contains solutions for working with Faye, IndexedDB, DHTMLX connectors, REST API, browser local storage, etc. At the same time users are encouraged to write their own integrations.

In this article we’ve  taken OData, an open data protocol for building RESTful API’s, and adapt Webix to its rules of requesting and modifying data

On the client side we’ve made loading and saving adjustments with the help of webix.proxy interface. As a serverside background we’ve taken DataBoom, a cloud database that supports OData protocol.

All in all,  Webix can be easily tuned to work with OData protocol to support the most common operations like retrieving and saving data, filtering and sorting it at the server-side as well as loading the data dynamically in case of a long dataset.

Do not miss the DataBoom service as well, it provides much more pleasant experience than conventional database and integrates with Webix very well. In addition to this, it is free for using it in small applications.