DevExtreme v24.1 is now available.

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

Your search did not match any results.

React Data Grid - Custom Summaries

The DataGrid can calculate custom summaries on the client or server side. In this demo, we have implemented client-side logic. The instructions below explain how to do this in your code.

Backend API
import React from 'react'; import DataGrid, { Column, Selection, Paging, Summary, TotalItem, DataGridTypes, } from 'devextreme-react/data-grid'; import { orders } from './data.ts'; const startupSelectedKeys = [1, 4, 7]; const calculateSelectedRow = (options: DataGridTypes.CustomSummaryInfo) => { if (options.name === 'SelectedRowsSummary') { if (options.summaryProcess === 'start') { options.totalValue = 0; } const isRowSelected = options.component.isRowSelected(options.value?.ID); if (options.summaryProcess === 'calculate' && isRowSelected) { options.totalValue += options.value.SaleAmount; } } }; const onSelectionChanged = (e: DataGridTypes.SelectionChangedEvent) => e.component.refresh(true); const App = () => ( <DataGrid id="gridContainer" defaultSelectedRowKeys={startupSelectedKeys} onSelectionChanged={onSelectionChanged} dataSource={orders} keyExpr="ID" showBorders={true}> <Paging enabled={false} /> <Selection mode="multiple" /> <Column dataField="OrderNumber" width={130} caption="Invoice Number" /> <Column dataField="OrderDate" dataType="date" /> <Column dataField="Employee" /> <Column dataField="CustomerStoreCity" caption="City" /> <Column dataField="CustomerStoreState" caption="State" /> <Column dataField="SaleAmount" width={160} alignment="right" format="currency" /> <Summary calculateCustomSummary={calculateSelectedRow}> <TotalItem name="SelectedRowsSummary" summaryType="custom" valueFormat="currency" displayFormat="Sum: {0}" showInColumn="SaleAmount" /> </Summary> </DataGrid> ); export default App;
import React from 'react'; import DataGrid, { Column, Selection, Paging, Summary, TotalItem, } from 'devextreme-react/data-grid'; import { orders } from './data.js'; const startupSelectedKeys = [1, 4, 7]; const calculateSelectedRow = (options) => { if (options.name === 'SelectedRowsSummary') { if (options.summaryProcess === 'start') { options.totalValue = 0; } const isRowSelected = options.component.isRowSelected(options.value?.ID); if (options.summaryProcess === 'calculate' && isRowSelected) { options.totalValue += options.value.SaleAmount; } } }; const onSelectionChanged = (e) => e.component.refresh(true); const App = () => ( <DataGrid id="gridContainer" defaultSelectedRowKeys={startupSelectedKeys} onSelectionChanged={onSelectionChanged} dataSource={orders} keyExpr="ID" showBorders={true} > <Paging enabled={false} /> <Selection mode="multiple" /> <Column dataField="OrderNumber" width={130} caption="Invoice Number" /> <Column dataField="OrderDate" dataType="date" /> <Column dataField="Employee" /> <Column dataField="CustomerStoreCity" caption="City" /> <Column dataField="CustomerStoreState" caption="State" /> <Column dataField="SaleAmount" width={160} alignment="right" format="currency" /> <Summary calculateCustomSummary={calculateSelectedRow}> <TotalItem name="SelectedRowsSummary" summaryType="custom" valueFormat="currency" displayFormat="Sum: {0}" showInColumn="SaleAmount" /> </Summary> </DataGrid> ); export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render( <App />, document.getElementById('app'), );
export const orders = [{ ID: 1, OrderNumber: 35703, OrderDate: '2014-04-10', SaleAmount: 11800, Terms: '15 Days', TotalAmount: 12175, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }, { ID: 4, OrderNumber: 35711, OrderDate: '2014-01-12', SaleAmount: 16050, Terms: '15 Days', TotalAmount: 16550, CustomerStoreState: 'California', CustomerStoreCity: 'San Jose', Employee: 'Jim Packard', }, { ID: 5, OrderNumber: 35714, OrderDate: '2014-01-22', SaleAmount: 14750, Terms: '15 Days', TotalAmount: 15250, CustomerStoreState: 'Nevada', CustomerStoreCity: 'Las Vegas', Employee: 'Harv Mudd', }, { ID: 7, OrderNumber: 35983, OrderDate: '2014-02-07', SaleAmount: 3725, Terms: '15 Days', TotalAmount: 3850, CustomerStoreState: 'Colorado', CustomerStoreCity: 'Denver', Employee: 'Todd Hoffman', }, { ID: 9, OrderNumber: 36987, OrderDate: '2014-03-11', SaleAmount: 14200, Terms: '15 Days', TotalAmount: 14800, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 11, OrderNumber: 38466, OrderDate: '2014-03-01', SaleAmount: 7800, Terms: '15 Days', TotalAmount: 8200, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }, { ID: 15, OrderNumber: 39874, OrderDate: '2014-02-04', SaleAmount: 9050, Terms: '30 Days', TotalAmount: 19100, CustomerStoreState: 'Nevada', CustomerStoreCity: 'Las Vegas', Employee: 'Harv Mudd', }, { ID: 18, OrderNumber: 42847, OrderDate: '2014-02-15', SaleAmount: 20400, Terms: '30 Days', TotalAmount: 20800, CustomerStoreState: 'Wyoming', CustomerStoreCity: 'Casper', Employee: 'Todd Hoffman', }, { ID: 19, OrderNumber: 43982, OrderDate: '2014-05-29', SaleAmount: 6050, Terms: '30 Days', TotalAmount: 6250, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 29, OrderNumber: 56272, OrderDate: '2014-02-06', SaleAmount: 15850, Terms: '30 Days', TotalAmount: 16350, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 30, OrderNumber: 57429, OrderDate: '2013-12-31', SaleAmount: 11050, Terms: '30 Days', TotalAmount: 11400, CustomerStoreState: 'Arizona', CustomerStoreCity: 'Phoenix', Employee: 'Clark Morgan', }, { ID: 32, OrderNumber: 58292, OrderDate: '2014-05-13', SaleAmount: 13500, Terms: '15 Days', TotalAmount: 13800, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }];
window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, jsx: 'react', }, meta: { 'react': { 'esModule': true, }, '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/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types@15.8.1/prop-types.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@24.1.6/cjs', 'devextreme-react': 'npm:devextreme-react@24.1.6/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.11/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.56/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.13', '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', 'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: 'json', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.13/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
export const orders = [ { ID: 1, OrderNumber: 35703, OrderDate: '2014-04-10', SaleAmount: 11800, Terms: '15 Days', TotalAmount: 12175, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }, { ID: 4, OrderNumber: 35711, OrderDate: '2014-01-12', SaleAmount: 16050, Terms: '15 Days', TotalAmount: 16550, CustomerStoreState: 'California', CustomerStoreCity: 'San Jose', Employee: 'Jim Packard', }, { ID: 5, OrderNumber: 35714, OrderDate: '2014-01-22', SaleAmount: 14750, Terms: '15 Days', TotalAmount: 15250, CustomerStoreState: 'Nevada', CustomerStoreCity: 'Las Vegas', Employee: 'Harv Mudd', }, { ID: 7, OrderNumber: 35983, OrderDate: '2014-02-07', SaleAmount: 3725, Terms: '15 Days', TotalAmount: 3850, CustomerStoreState: 'Colorado', CustomerStoreCity: 'Denver', Employee: 'Todd Hoffman', }, { ID: 9, OrderNumber: 36987, OrderDate: '2014-03-11', SaleAmount: 14200, Terms: '15 Days', TotalAmount: 14800, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 11, OrderNumber: 38466, OrderDate: '2014-03-01', SaleAmount: 7800, Terms: '15 Days', TotalAmount: 8200, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }, { ID: 15, OrderNumber: 39874, OrderDate: '2014-02-04', SaleAmount: 9050, Terms: '30 Days', TotalAmount: 19100, CustomerStoreState: 'Nevada', CustomerStoreCity: 'Las Vegas', Employee: 'Harv Mudd', }, { ID: 18, OrderNumber: 42847, OrderDate: '2014-02-15', SaleAmount: 20400, Terms: '30 Days', TotalAmount: 20800, CustomerStoreState: 'Wyoming', CustomerStoreCity: 'Casper', Employee: 'Todd Hoffman', }, { ID: 19, OrderNumber: 43982, OrderDate: '2014-05-29', SaleAmount: 6050, Terms: '30 Days', TotalAmount: 6250, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 29, OrderNumber: 56272, OrderDate: '2014-02-06', SaleAmount: 15850, Terms: '30 Days', TotalAmount: 16350, CustomerStoreState: 'Utah', CustomerStoreCity: 'Salt Lake City', Employee: 'Clark Morgan', }, { ID: 30, OrderNumber: 57429, OrderDate: '2013-12-31', SaleAmount: 11050, Terms: '30 Days', TotalAmount: 11400, CustomerStoreState: 'Arizona', CustomerStoreCity: 'Phoenix', Employee: 'Clark Morgan', }, { ID: 32, OrderNumber: 58292, OrderDate: '2014-05-13', SaleAmount: 13500, Terms: '15 Days', TotalAmount: 13800, CustomerStoreState: 'California', CustomerStoreCity: 'Los Angeles', Employee: 'Harv Mudd', }, ];
<!DOCTYPE html> <html lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.1.6/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
#gridContainer { height: 440px; margin-bottom: 10px; }
  1. Make sure that the remoteOperations.summary, remoteOperations.groupPaging, or remoteOperations property is not set or set to false.

  2. Add a summary configuration object to the summary.groupItems or summary.totalItems array.

  3. Set the object's summaryType to "custom".

  4. Specify the summary's name. It will be used to identify the summary item within the calculateCustomSummary function in the next step.

  5. Calculate the resulting value in the calculateCustomSummary function. Implement the necessary calculation stages. See the function's description for details.

The custom summary in this example calculates the sum of sale amounts for selected rows. To recalculate the resulting value when selection is changed, the code calls the refresh() method in the onSelectionChanged handler.

Client-side custom summaries are suitable for small datasets. If your tests show that client-side calculations result in noticeable lags, we recommend that you use Server-Side Data Aggregation.