Custom Sources

To consume data from a custom source, the DataGrid uses the CustomStore. This article provides details on how to configure it and on the protocol that the CustomStore adheres when communicating with the server. If the server already processes data, that is, performs filtering, sorting, paging, grouping or summary calculation, notify the DataGrid of it by assigning true to the corresponding field of the remoteOperations object.

JavaScript
$(function() {
    $("#gridContainer").dxDataGrid({
        //...
        remoteOperations: {
            filtering: true,
            sorting: true,
            paging: true,
            grouping: true,
            summary: true
        }
        // or simply
        // remoteOperations: true
    });
}); 

If the server does not process data yet, employ one of the following extensions by DevExtreme. They implement server-side data processing and also configure the CustomStore for you. Remember to notify the DataGrid of the data processing operations that were delegated to the server.

If these extensions do not suit your needs, configure the CustomStore and implement server-side data processing by yourself following the instructions given in this article. Note that the server may leave some of the data processing operations unimplemented. In this case, make sure that the corresponding fields of the remoteOperations object are set to false.

See Also

Load Data

To load data from the server, the CustomStore needs the load function. This function accepts a bag of loadOptions and passes them to the server. The server must process data according to the loadOptions and send processed data back. The members of the loadOptions depend on which data processing operations the DataGrid delegates to the server.

The example below shows how to implement the load function for all data processing operations. Note that in this example, the CustomStore is not declared explicitly. Instead, CustomStore operations are implemented directly in the DataSource configuration object to shorten the example.

JavaScript
var gridDataSource = new DevExpress.data.DataSource({
    load: function (loadOptions) {
        var d = $.Deferred();
        $.getJSON('http://mydomain.com/MyDataService', {  
            // Passing settings to the server

            filter: loadOptions.filter ? JSON.stringify(loadOptions.filter) : "", // Pass if filtering is remote
            sort: loadOptions.sort ? JSON.stringify(loadOptions.sort) : "",       // Pass if sorting is remote
            // Pass if paging is remote
            skip: loadOptions.skip,     // The number of records to skip
            take: loadOptions.take,     // The number of records to take
            requireTotalCount: loadOptions.requireTotalCount,   // A flag telling the server whether
                                                                // the total count of records (totalCount) is required
            group: loadOptions.group ? JSON.stringify(loadOptions.group) : "", // Pass if grouping is remote
            totalSummary: loadOptions.totalSummary, // Pass if summary is calculated remotely
            groupSummary: loadOptions.groupSummary  // Pass if grouping is remote and summary is calculated remotely
        }).done(function (result) {
            // You can process the received data here
            d.resolve(result.data, { 
                totalCount: result.totalCount, // The count of received records; needed if paging is enabled
                summary: result.summary        // Needed if summary is calculated remotely
            });
        });
        return d.promise();
    }
});

$(function() {
    $("#gridContainer").dxDataGrid({
        dataSource: gridDataSource,
        // ...
    });
});

The object passed with the $.getJSON() request has the following structure.

JavaScript
{
    filter: [
        [ "dataFieldName1", "operator", "value" ],
        "and", // "or"
        [ "dataFieldName2", "operator", "value" ],
        // ...
    ],
    sort: [
        { selector : "dataFieldName1", desc : true },
        { selector : "dataFieldName2", desc : false },
        // ...
    ],
    skip: 0,
    take: 20,
    requireTotalCount: true,
    group: [
        // Group expression for numbers
        { selector: "dataFieldName1", groupInterval: 100, desc: false },
        // Group expression for dates
        { selector: "dataFieldName2", groupInterval: "year", desc: false },
        { selector: "dataFieldName2", groupInterval: "month", desc: false },
        // Group expression for strings
        { selector: "dataFieldName3", desc: true },
        // ...
    ],
    totalSummary: [
        { selector: "dataFieldName1", summaryType: "sum" }, 
        { selector: "dataFieldName2", summaryType: "min" },
        // ... 
    ],
    groupSummary: [
        { selector: "dataFieldName1", summaryType: "sum" }, 
        { selector: "dataFieldName2", summaryType: "min" },
        // ... 
    ]

}
NOTE
The group expression contains the groupInterval parameter only when the DataGrid sends a request for the data source of the header filter, and only if this data source contains dates or numbers. Note that in case of numbers, the groupInterval option should be specified explicitly.

After receiving this object with settings, the server should apply them to data, and then send back an object of the following structure.

{
    data: [{
        key: "Group 1",
        // Group summary
        summary: [30, 20, 40],  // Summary values should be in the same order as items
                                // in the summary | groupItems array of the DataGrid configuration
        items: [{
            key: "Group 1_1",
            summary: [12, 5, 19],
            items: [
                key: "Group 1_1_1",
                summary: [8, 2, 10],
                // This is a group of the deepest hierarchy level,
                // therefore, you need to return the following fields
                items: null,
                count: 3        // The count of data rows in the current group
            ]
        }, {
            key: "Group 1_2",
            summary: [18, 15, 21],
            items: [ ... ]
        }]
    }, {
        key: "Group 2",
        summary: [100, 50, 60],
        items: [ ... ]
    }, 
        // . . .
    ],
    // The total count of records after applying the filter expression (if any was received)
    // Needed only if requireTotalCount was true (see the previous code)
    totalCount: 200,
    // Total summary 
    summary: [170, 20, 20, 1020] // Summary values should be in the same order as items
                                 // in the summary | totalItems array of the DataGrid configuration
}
NOTE
If the server has not recieved the group setting, the data field should contain data objects only.

If grouping large data seems to lower the performance of the DataGrid, consider employing the remote group paging feature. Note that for this feature, the server and the client sides should be configured in a slightly different manner. For details, refer to the Remote Group Paging topic.

Add, Delete, Update Data

To allow a user to add, delete and update data in the DataGrid, assign true to the corresponding field of the editing object.

JavaScript
$(function(){
    $("#gridContainer").dxDataGrid({
        // ...
        editing: {
            allowUpdating: true, 
            allowDeleting: true, 
            allowAdding: true
        }
    });
});

With these settings, the DataGrid expects that the server can also add, update and delete data. In addition, you need to configure the CustomStore as shown below. Note that in this example, the CustomStore is not declared explicitly. Instead, CustomStore operations are implemented directly in the DataSource configuration object to shorten the example.

JavaScript
var gridDataSource = new DevExpress.data.DataSource({
    // ...
    insert: function (values) {
        return $.ajax({
            url: "http://mydomain.com/MyDataService/",
            method: "POST",
            data: values
        })
    },
    remove: function (key) {
        return $.ajax({
            url: "http://mydomain.com/MyDataService/" + encodeURIComponent(key),
            method: "DELETE",
        })
    },
    update: function (key, values) {
        return $.ajax({
            url: "http://mydomain.com/MyDataService/" + encodeURIComponent(key),
            method: "PUT",
            data: values
        })
    }
});

$(function() {
    $("#gridContainer").dxDataGrid({
        dataSource: gridDataSource,
        // ...
    });
});