DevExtreme Angular - Custom Data Sources (2024)

The communication between the CustomStore and the server is organized as follows:

Each setting carries information about data operation (sorting, filtering, etc.) and is present only if this operation is enabled or declared as remote. Configure remoteOperations in the DataGrid, TreeList, and PivotGridDataSource UI components or remoteFiltering in the Scheduler UI component to declare remote operations. For other UI components, use DataSource properties to enable operations.

The following example shows a CustomStore that sends data processing settings to the server. The settings are passed as the loadOptions parameter to the load function. loadOptions may contain empty settings if an operation is disabled or local. In this example, the forEach loop filters out such settings:

jQuery
$(function() { var customDataSource = new DevExpress.data.CustomStore({ key: "ID", load: function(loadOptions) { var d = $.Deferred(); var params = {}; [ "filter", "group", "groupSummary", "parentIds", "requireGroupCount", "requireTotalCount", "searchExpr", "searchOperation", "searchValue", "select", "sort", "skip", "take", "totalSummary", "userData" ].forEach(function(i) { if(i in loadOptions && isNotEmpty(loadOptions[i])) { params[i] = JSON.stringify(loadOptions[i]); } }); $.getJSON("https://mydomain.com/MyDataService", params) .done(function(response) { d.resolve(response.data, { totalCount: response.totalCount, summary: response.summary, groupCount: response.groupCount }); }) .fail(function() { throw "Data loading error" }); return d.promise(); }, // Needed to process selected value(s) in the SelectBox, Lookup, Autocomplete, and DropDownBox // byKey: function(key) { // var d = new $.Deferred(); // $.get('https://mydomain.com/MyDataService?id=' + key) // .done(function(result) { // d.resolve(result); // }); // return d.promise(); // } }); $("#dataGridContainer").dxDataGrid({ dataSource: customDataSource, remoteOperations: { groupPaging: true } });});function isNotEmpty(value) { return value !== undefined && value !== null && value !== "";}
Angular

app.component.ts

app.component.html

app.module.ts

import { Component } from '@angular/core';import { HttpClient, HttpParams } from '@angular/common/http'; import { lastValueFrom } from 'rxjs';import DataSource from 'devextreme/data/data_source';import CustomStore from 'devextreme/data/custom_store'; import { LoadOptions } from 'devextreme/data';@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [HttpClient]})export class AppComponent { customDataSource: DataSource; constructor(private http: HttpClient) { const isNotEmpty = (value: unknown) => value !== undefined && value !== null && value !== ''; this.customDataSource = new DataSource({ store: new CustomStore({ key: 'ID', load: (loadOptions: LoadOptions) => { let params: HttpParams = new HttpParams(); [ 'filter', 'group', 'groupSummary', 'parentIds', 'requireGroupCount', 'requireTotalCount', 'searchExpr', 'searchOperation', 'searchValue', 'select', 'sort', 'skip', 'take', 'totalSummary', 'userData', ].forEach(function (i) { const optionValue = loadOptions[i as keyof LoadOptions]; if (i in loadOptions && isNotEmpty(optionValue)) { params = params.set(i, JSON.stringify(optionValue)); } }); return lastValueFrom( this.http.get( 'https://mydomain.com/MyDataService', { params } ) ).then((response: any) => { return { data: response?.data, totalCount: response?.totalCount, summary: response?.summary, groupCount: response?.groupCount, }; }).catch(() => { throw 'Data loading error'; }); }, // Needed to process selected value(s) in the SelectBox, Lookup, Autocomplete, and DropDownBox // byKey: (key: number) => { // return lastValueFrom( // this.http.get(`$https://mydomain.com/MyDataService?id=${key}`) // ); // }, }), }); }}
<dx-data-grid [dataSource]="customDataSource"> <dxo-remote-operations [groupPaging]="true"> </dxo-remote-operations></dx-data-grid>
import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppComponent } from './app.component';import { HttpClientModule } from '@angular/common/http';import { DxDataGridModule } from 'devextreme-angular';@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule, DxDataGridModule ], providers: [ ], bootstrap: [AppComponent]})export class AppModule { }
Vue

App.vue

<template> <DxDataGrid :data-source="customDataSource"> <DxRemoteOperations :group-paging="true" /> </DxDataGrid></template><script>import 'devextreme/dist/css/dx.light.css';import DxDataGrid, { DxRemoteOperations} from 'devextreme-vue/data-grid';import CustomStore from 'devextreme/data/custom_store';import 'whatwg-fetch';const isNotEmpty = (value) => value !== undefined && value !== null && value !== '';function handleErrors(response) { if (!response.ok) { throw Error(response.statusText); } return response;}const customDataSource = new CustomStore({ key: 'ID', load: (loadOptions) => { let params = '?'; [ 'filter', 'group', 'groupSummary', 'parentIds', 'requireGroupCount', 'requireTotalCount', 'searchExpr', 'searchOperation', 'searchValue', 'select', 'sort', 'skip', 'take', 'totalSummary', 'userData' ].forEach(function(i) { if(i in loadOptions && isNotEmpty(loadOptions[i])) { params += `${i}=${JSON.stringify(loadOptions[i])}&`; } }); params = params.slice(0, -1); return fetch(`https://mydomain.com/MyDataService${params}`) .then(handleErrors) .then(response => response.json()) .then(response => { return { data: response.data, totalCount: response.totalCount, summary: response.summary, groupCount: response.groupCount }; }) .catch(() => { throw 'Network error' }); }, // Needed to process selected value(s) in the SelectBox, Lookup, Autocomplete, and DropDownBox // byKey: (key) => { // return fetch(`https://mydomain.com/MyDataService?id=${key}`) // .then(handleErrors); // }});export default { components: { DxDataGrid, DxRemoteOperations }, data() { return { customDataSource } }}</script>
React

App.js

import React from 'react';import 'devextreme/dist/css/dx.light.css';import DataGrid, { RemoteOperations } from 'devextreme-react/data-grid';import CustomStore from 'devextreme/data/custom_store';import 'whatwg-fetch';const isNotEmpty = (value) => value !== undefined && value !== null && value !== '';function handleErrors(response) { if (!response.ok) { throw Error(response.statusText); } return response;}const customDataSource = new CustomStore({ key: 'ID', load: (loadOptions) => { let params = '?'; [ 'filter', 'group', 'groupSummary', 'parentIds', 'requireGroupCount', 'requireTotalCount', 'searchExpr', 'searchOperation', 'searchValue', 'select', 'sort', 'skip', 'take', 'totalSummary', 'userData' ].forEach(function(i) { if(i in loadOptions && isNotEmpty(loadOptions[i])) { params += `${i}=${JSON.stringify(loadOptions[i])}&`; } }); params = params.slice(0, -1); return fetch(`https://mydomain.com/MyDataService${params}`) .then(handleErrors) .then(response => response.json()) .then(response => { return { data: response.data, totalCount: response.totalCount, summary: response.summary, groupCount: response.groupCount }; }) .catch(() => { throw 'Network error' }); }, // Needed to process selected value(s) in the SelectBox, Lookup, Autocomplete, and DropDownBox // byKey: (key) => { // return fetch(`https://mydomain.com/MyDataService?id=${key}`) // .then(handleErrors); // }});class App extends React.Component { render() { return ( <DataGrid dataSource={customDataSource}> <RemoteOperations groupPaging={true}> </DataGrid> ); }}export default App;

When the server receives the data processing settings, it should apply them to the dataset and send back an object with the following structure:

{ data: [{ key: "Group 1", items: [ ... ], // subgroups or data objects if there are no further subgroups (check group.isExpanded = true) // is null if group.isExpanded = false count: 3, // count of items in this group; required only when items = null summary: [30, 20, 40] // group summary results }, ... ], totalCount: 200, // if requireTotalCount = true summary: [170, 20, 20, 1020], // total summary results groupCount: 35 // if requireGroupCount = true}

If the server did not receive the group setting, the structure should be different:

DevExtreme Angular - Custom Data Sources (2024)
Top Articles
Latest Posts
Article information

Author: Duane Harber

Last Updated:

Views: 5611

Rating: 4 / 5 (71 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Duane Harber

Birthday: 1999-10-17

Address: Apt. 404 9899 Magnolia Roads, Port Royceville, ID 78186

Phone: +186911129794335

Job: Human Hospitality Planner

Hobby: Listening to music, Orienteering, Knapping, Dance, Mountain biking, Fishing, Pottery

Introduction: My name is Duane Harber, I am a modern, clever, handsome, fair, agreeable, inexpensive, beautiful person who loves writing and wants to share my knowledge and understanding with you.