DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Real-Time Updates

The DataGrid component is optimized for real-time updates. You should bind it to a DataSource that contains a store and use the push(changes) method to update the store's data. The rows with changed data are rerendered (see the DataGrid's repaintChangesOnly property). The DataSource object also provides properties that control its behavior when it receives a push. For example, reshapeOnPush is set to true in this demo to allow summaries to be recalculated.

Grids in the detail sections are updated in real time as well. All the grids share the same ordersStore that receives pushes, but it is filtered to show only a specific product's orders for each grid.

Backend API
<dx-data-grid id="orders" [dataSource]="productsDataSource" [repaintChangesOnly]="true" [columnAutoWidth]="true" [showBorders]="true" > <dxo-paging [pageSize]="10"></dxo-paging> <dxi-column dataField="ProductName" dataType="string"></dxi-column> <dxi-column dataField="UnitPrice" dataType="number" format="currency" ></dxi-column> <dxi-column dataField="OrderCount" dataType="number"></dxi-column> <dxi-column dataField="Quantity" dataType="number"></dxi-column> <dxi-column dataField="Amount" dataType="number" format="currency" [allowSorting]="true" ></dxi-column> <dxo-summary> <dxi-total-item column="ProductName" summaryType="count"></dxi-total-item> <dxi-total-item column="Amount" summaryType="sum" displayFormat="{0}" valueFormat="currency" ></dxi-total-item> <dxi-total-item column="OrderCount" summaryType="sum" displayFormat="{0}" ></dxi-total-item> </dxo-summary> <dxo-master-detail [enabled]="true" template="productDetail"> </dxo-master-detail> <div *dxTemplate="let detail of 'productDetail'"> <dx-data-grid [dataSource]="getDetailGridDataSource(detail.data)" [columnAutoWidth]="true" [repaintChangesOnly]="true" [showBorders]="true" > <dxo-paging [pageSize]="5"></dxo-paging> <dxi-column dataField="OrderID" dataType="number"></dxi-column> <dxi-column dataField="ShipCity" dataType="string"></dxi-column> <dxi-column dataField="OrderDate" dataType="datetime" format="yyyy/MM/dd HH:mm:ss" ></dxi-column> <dxi-column dataField="UnitPrice" dataType="number" format="currency" ></dxi-column> <dxi-column dataField="Quantity" dataType="number"></dxi-column> <dxi-column caption="Amount" dataType="number" format="currency" [allowSorting]="true" [calculateCellValue]="getAmount" ></dxi-column> <dxo-summary> <dxi-total-item column="OrderID" summaryType="count"></dxi-total-item> <dxi-total-item column="Quantity" summaryType="sum" displayFormat="{0}" ></dxi-total-item> <dxi-total-item column="Amount" summaryType="sum" displayFormat="{0}" valueFormat="currency" ></dxi-total-item> </dxo-summary> </dx-data-grid> </div> </dx-data-grid> <div class="options"> <div class="caption">Options</div> <div class="option"> <span>Update frequency:</span> <dx-slider [min]="10" [step]="10" [max]="5000" [(value)]="updatesPerSecond"> <dxo-tooltip [enabled]="true" format="#0 per second" showMode="always" position="top" > </dxo-tooltip> </dx-slider> </div> </div>
import { NgModule, Component, enableProdMode, ChangeDetectionStrategy, } from '@angular/core'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { DxDataGridModule, DxSliderModule } from 'devextreme-angular'; import DataSource from 'devextreme/data/data_source'; import ArrayStore from 'devextreme/data/array_store'; import { Product, Order, Service } from './app.service'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], providers: [Service], changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent { productsDataSource: DataSource; orders: ArrayStore; updatesPerSecond = 100; constructor(service: Service) { this.productsDataSource = new DataSource({ store: service.getProducts(), reshapeOnPush: true, }); this.orders = service.getOrders(); setInterval(() => { if (service.getOrderCount() > 500000) { return; } for (let i = 0; i < this.updatesPerSecond / 20; i++) { service.addOrder(); } }, 50); } getDetailGridDataSource(product: Product) { return new DataSource({ store: this.orders, reshapeOnPush: true, filter: ['ProductID', '=', product.ProductID], }); } getAmount(order: Order) { return order.UnitPrice * order.Quantity; } } @NgModule({ imports: [ BrowserModule, BrowserTransferStateModule, DxDataGridModule, DxSliderModule, ], declarations: [AppComponent], bootstrap: [AppComponent], }) export class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
::ng-deep .options { padding: 20px; margin-top: 20px; background-color: rgba(191, 191, 191, 0.15); } ::ng-deep .caption { font-weight: 500; font-size: 18px; } ::ng-deep .option { margin-top: 10px; display: flex; align-items: center; } ::ng-deep .option > span { position: relative; top: 2px; margin-right: 10px; } ::ng-deep .option > .dx-widget { width: 500px; display: inline-block; vertical-align: middle; }
import { Injectable } from '@angular/core'; import ArrayStore from 'devextreme/data/array_store'; export class Product { ProductID: number; ProductName: string; UnitPrice: number; Quantity: number; Amount: number; OrderCount: number; } export class Order { OrderID: number; ShipCity: string; ProductID: number; UnitPrice: number; OrderDate: Date; Quantity: number; } const cities = ['Los Angeles', 'Denver', 'Bentonville', 'Atlanta', 'Reno', 'Beaver', 'Malibu', 'Phoenix', 'San Diego', 'Little Rock', 'Pasadena', 'Boise', 'San Jose', 'Chatsworth', 'San Fernando', 'South Pasadena', 'San Fernando Valley', 'La Canada', 'St. Louis']; const products: Product[] = []; for (let i = 1; i <= 100; i++) { products.push({ ProductID: i, ProductName: `Product ${i}`, UnitPrice: Math.floor(Math.random() * 1000) + 1, Quantity: 0, Amount: 0, OrderCount: 0, }); } const productsStore = new ArrayStore({ data: products, key: 'ProductID', }); const orders: Order[] = []; const ordersStore = new ArrayStore({ key: 'OrderID', data: orders, }); @Injectable() export class Service { getProducts() { return productsStore; } getOrders() { return ordersStore; } getOrderCount() { return orders.length; } addOrder() { const product = products[Math.round(Math.random() * 99)]; const order: Order = { OrderID: orders.length ? orders[orders.length - 1].OrderID + 1 : 20001, ShipCity: cities[Math.round(Math.random() * (cities.length - 1))], ProductID: product.ProductID, UnitPrice: product.UnitPrice, OrderDate: new Date(), Quantity: Math.round(Math.random() * 20) + 1, }; ordersStore.push([{ type: 'insert', data: order }]); productsStore.push([{ type: 'update', key: order.ProductID, data: { OrderCount: product.OrderCount + 1, Quantity: product.Quantity + order.Quantity, Amount: product.Amount + order.UnitPrice * order.Quantity, }, }]); } }
// In real applications, you should not transpile code in the browser. // You can see how to create your own application with Angular and DevExtreme here: // https://js.devexpress.com/Documentation/Guide/Angular_Components/Getting_Started/Create_a_DevExtreme_Application/ window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, }, meta: { 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', '@angular/core': 'npm:@angular/core@12.2.17', '@angular/platform-browser': 'npm:@angular/platform-browser@12.2.17', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@12.2.17', '@angular/forms': 'npm:@angular/forms@12.2.17', '@angular/common': 'npm:@angular/common@12.2.17', '@angular/compiler': 'npm:@angular/compiler@12.2.17', 'tslib': 'npm:tslib@2.6.2/tslib.js', 'rxjs': 'npm:rxjs@7.5.3/dist/bundles/rxjs.umd.js', 'rxjs/operators': 'npm:rxjs@7.5.3/dist/cjs/operators/index.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme/bundles/dx.all': 'npm:devextreme@23.2.5/bundles/dx.all.js', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51', 'devextreme-angular': 'npm:devextreme-angular@23.2.5', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'app': { main: './app.component.ts', defaultExtension: 'ts', }, 'devextreme': { defaultExtension: 'js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, 'rxjs': { defaultExtension: 'js', }, 'rxjs/operators': { defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', 'npm:@angular/*/package.json', 'npm:@angular/common@12.2.17/*/package.json', 'npm:rxjs@7.5.3/package.json', 'npm:rxjs@7.5.3/operators/package.json', 'npm:devextreme-angular@23.2.5/*/package.json', 'npm:devextreme-angular@23.2.5/ui/*/package.json', 'npm:devextreme-angular@23.2.5/package.json', 'npm:devexpress-diagram@2.2.5/package.json', 'npm:devexpress-gantt@4.1.51/package.json', ], }; System.config(window.config);
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.12.0/dist/zone.js"></script> <script src="https://unpkg.com/reflect-metadata@0.1.13/Reflect.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script src="config.js"></script> <script> System.import("app").catch(console.error.bind(console)); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> </body> </html>