添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

The UI component switches to the active state when users press down the primary mouse button. When this property is set to true , the CSS rules for the active state apply. You can change these rules to customize the component.

Use this property when you display the component on a platform whose guidelines include the active state change for UI components.

Initially, columns appear in the order specified by the columns array. If you skip specifying this array, columns will mirror the order of fields in the first object from the dataSource . You can allow a user to reorder columns at runtime by setting the allowColumnReordering property to true .

DataGrid Demo TreeList Demo

See Also
  • columns[] . allowReordering
  • By default, the width of each column depends on the width of the UI component and the total number of columns . You can allow a user to resize the columns at runtime by setting the allowColumnResizing property to true .

    DataGrid Demo TreeList Demo

    See Also
  • columnResizingMode
  • columns[] . allowResizing
  • columns[] . width
  • NOTE
    You must specify the component's height to ensure that the autoNavigateToFocusedRow property works properly.

    If you set the remoteOperations property to true , the DataGrid generates additional requests with comparison operators (for example, < and > ). This logic does not work if ODataStore is bound to a table with GUID keys. You need to disable the autoNavigateToFocusedRow or remoteOperations properties to ensure it operates correctly.

    When this property is set to true , data loaded once is saved in cache. Then, the UI component takes data from this cache when performing such operations as sorting, grouping, paging, etc. Caching is helpful when the data source takes significant time to load. But, consider disabling it for frequently changing data sources.

    To update data in cache, call the refresh() method of the UI component or the reload() method of the DataSource :

    jQuery
    JavaScript
    $("#dataGridContainer").dxDataGrid("refresh");
    // ===== or =====
    const dataGridDataSource = $("#dataGridContainer").dxDataGrid("getDataSource");
    dataGridDataSource.reload();
    Angular
    TypeScript
    import { ..., ViewChild } from "@angular/core";
    import { DxDataGridModule, DxDataGridComponent } from "devextreme-angular";
    // ...
    export class AppComponent {
        @ViewChild(DxDataGridComponent, { static: false }) dataGrid: DxDataGridComponent;
        // Prior to Angular 8
        // @ViewChild(DxDataGridComponent) dataGrid: DxDataGridComponent;
        refreshData () {
            this.dataGrid.instance.refresh();
            // ===== or =====
            const dataGridDataSource = this.dataGrid.instance.getDataSource();
            dataGridDataSource.reload();
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid :ref="dataGridRefKey">
            <!-- ... -->
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    const dataGridRefKey = "my-data-grid";
    export default {
        components: {
            DxDataGrid
        data() {
            return {
                dataGridRefKey
        methods: {
            refreshData() {
                this.dataGrid.refresh();
                // ===== or =====
                const dataGridDataSource = this.dataGrid.getDataSource();
                dataGridDataSource.reload();
        computed: {
            dataGrid: function() {
                return this.$refs[dataGridRefKey].instance;
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.dataGridRef = React.createRef();
            this.refreshData = () => {
                this.dataGrid.refresh();
                // ===== or =====
                const dataGridDataSource = this.dataGrid.getDataSource();
                dataGridDataSource.reload();
        get dataGrid() {
            return this.dataGridRef.current.instance();
        render() {
            return (
                <DataGrid ref={this.dataGridRef}>
                    {/* ... */ }
                </DataGrid>
    export default App;
    NOTE
    If you fetch data from the server, some operations with data can be executed remotely , while others - locally. If you perform basic operations (sorting, filtering, and paging) remotely and advanced operations (grouping and summary calculation) locally, certain user actions will force the DataGrid to query the server for data repeatedly despite caching being enabled. Particularly, the advanced operations demand data to be reloaded completely from the server to provide correct results.
    See Also
  • Enhance Performance on Large Datasets
  • When this property is set to true , all columns adjust their width to the content.

    If the DataGrid is wider than its overall content, the columns are stretched to occupy all available width. To avoid this, set the columnWidth or columns . width property to "auto" .

    If the content is wider, the columnAutoWidth property set to true causes horizontal scrolling. You can set the allowHiding property to false for columns you want to be displayed continuously.

    When the columnAutoWidth property is set to false , all columns have identical width, which in turn depends on the width of the UI component.

    DataGrid Demo TreeList Demo

    See Also
  • width
  • columnFixing
  • wordWrapEnabled
  • When the width of all columns exceeds the UI component width, horizontal scrolling appears. If specific columns should be on screen constantly regardless of how far the UI component is scrolled, allow a user to fix them at runtime using the context menu. For this, set the columnFixing . enabled property to true .

    When you enable column fixing, command columns become fixed automatically.

    DataGrid Demo TreeList Demo

    See Also
  • Column Fixing
  • This property set to true makes the UI component hide certain columns automatically if all the columns do not fit the UI component's width. Columns with low hidingPriority are hidden first. These are the rightmost (leftmost if rtlEnabled is true ) columns by default. Information from hidden columns is available in an adaptive detail row.

    DataGrid Demo TreeList Demo

    NOTE
    If you set this property to true and columnResizingMode is set to 'nextColumn' (default), then also enable columnAutoWidth to ensure the component works properly.
    See Also
  • onAdaptiveDetailRowPreparing
  • Adaptability
  • When a user resizes a column, the width of the next column changes.
  • widget
    When a user resizes a column, the width of the UI component changes.
    This mode is ignored if you specify the width of any column in percent.
  • DataGrid Demo TreeList Demo

    NOTE
    If this property is set to 'nextColumn' (default) and you enable the columnHidingEnabled property, then also enable columnAutoWidth to ensure the component works properly.

    By default, a column is created for each field of a data source object, but in most cases, it is redundant. To specify a set of columns to be created in a grid, assign an array specifying these columns to the columns property. Each grid column is represented in this array by an object containing column settings or by a data source field that this column is bound to. Detailed information on specifying grid columns is given in the Columns Overview article.

    Column properties define the behavior and appearance of a grid column. One of the other capabilities allows you to control the sorting of column values using the allowSorting and sortOrder properties, apply a filter to grid records using the allowFiltering and filterOperations properties, and group grid records using the allowGrouping and groupIndex properties. In addition, you can change the visibility and width of a column using corresponding properties.

    To get or set a property or several properties for a column at runtime, use the columnOption method with the required arguments.

    Use this function to make minor adjustments to automatically generated columns. You can access and modify column configurations using the function's parameter.

    jQuery
    JavaScript
    $(function(){
        $("#dataGrid").dxDataGrid({
            // ...
            customizeColumns: function (columns) {
                columns[0].width = 100;
                columns[1].width = 210;
    
    Angular
    TypeScript
    HTML
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        customizeColumns (columns) {
            columns[0].width = 100;
            columns[1].width = 210;
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    <dx-data-grid ...
        [customizeColumns]="customizeColumns">
    </dx-data-grid>
    App.vue
    <template>
        <DxDataGrid ...
            :customize-columns="customizeColumns"> 
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid, {
        // ... 
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
            customizeColumns(columns) {
                columns[0].width = 100;
                columns[1].width = 210;
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        // ...
    } from 'devextreme-react/data-grid';
    class App extends React.Component {
        customizeColumns = (columns) => {
            columns[0].width = 100;
            columns[1].width = 210;
        render() {
            return (
                <DataGrid ...
                    customizeColumns={this.customizeColumns}
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        .CustomizeColumns("customizeColumns")
    <script>
        function customizeColumns(columns) {
            columns[0].width = 100;
            columns[1].width = 210;
    </script>
    NOTE
    Data operations (sorting, filtering, summary) are unavailable for the columns created via customizeColumns . To create a fully functioning column, add it to the columns array.
    React

    Note that the [element Name] Render and [element Name] Component (for example, the cellRender and cellComponent ) do not work within the customizeColumn function. Instead, use the columns array.

    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        // ...
    } from 'devextreme-react/data-grid';
    class App extends React.Component {
        customizeColumns = (columns) => {
            // ...
            // This code does not work
            // columns[0].cellRender = cellRender;
        render() {
            return (
                <DataGrid ...
                    customizeColumns={this.customizeColumns}
                    <!-- ... -->
                    <Column
                        dataField="Picture"
                        cellRender={cellRender} <!-- This code works correctly -->
                </DataGrid>
    function cellRender(data) {
        return <img src={data.value} />;
    export default App;
  • Disable column reordering , grouping , and column fixing when you specify the row template. Its content cannot automatically synchronize with the column layout, which makes these features inoperative. Command columns are not supported either.

  • You should implement the following features manually: editing , adaptability , selection , master-detail interface , and focused row . Follow the links to review the API that can help you with this task.

  • When DataGrid is exported to Excel, it omits customizations made in the template. However, you can use ExcelJS API to recreate the changes in the exported file. Use the customizeCell function to do this.

  • You should specify all CSS rules manually if you use this property. Refer to the following demo's styles.css tab to see an example:

  • The DataGrid works with collections of objects. We recommend that you use a plain object in data (no circulars, no prototypes, etc.). The DataGird uses JSON.stringify and other recursive methods. Circular object references can crash native recursive algorithms (such as serializers) with stack overflows.

    Depending on your data source, bind DataGrid to data as follows.

  • Data Array
    Assign the array to the dataSource option and specify the keyExpr . View Demo

  • Read-Only Data in JSON Format
    Set the dataSource property to the URL of a JSON file or service that returns JSON data. View Demo

  • OData
    Implement an ODataStore . Make sure to specify the key . View Demo

  • Web API, PHP, MongoDB
    Use one of the following extensions to enable the server to process data according to the protocol DevExtreme UI components use:

  • DevExtreme.AspNet.Data
  • DevExtreme-PHP-Data
  • devextreme-query-mongodb
  • Then, use the createStore method to configure access to the server on the client as shown below. This method is part of DevExtreme.AspNet.Data .

    jQuery
    JavaScript
    $(function() {
        let serviceUrl = "https://url/to/my/service";
        $("#dataGridContainer").dxDataGrid({
            // ...
            dataSource: DevExpress.data.AspNet.createStore({
                key: "ID",
                loadUrl: serviceUrl + "/GetAction",
                insertUrl: serviceUrl + "/InsertAction",
                updateUrl: serviceUrl + "/UpdateAction",
                deleteUrl: serviceUrl + "/DeleteAction"
    
    Angular
    app.component.ts
    app.component.html
    app.module.ts
    import { Component } from '@angular/core';
    import CustomStore from 'devextreme/data/custom_store';
    import { createStore } from 'devextreme-aspnet-data-nojquery';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        store: CustomStore;
        constructor() {
            let serviceUrl = "https://url/to/my/service";
            this.store = createStore({
                key: "ID",
                loadUrl: serviceUrl + "/GetAction",
                insertUrl: serviceUrl + "/InsertAction",
                updateUrl: serviceUrl + "/UpdateAction",
                deleteUrl: serviceUrl + "/DeleteAction"
    <dx-data-grid ...
        [dataSource]="store">
    </dx-data-grid>
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template> 
        <DxDataGrid ...
            :data-source="store" />
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import CustomStore from 'devextreme/data/custom_store';
    import { createStore } from 'devextreme-aspnet-data-nojquery';
    import { DxDataGrid } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        data() {
            const serviceUrl = "https://url/to/my/service";
            const store = createStore({
                key: "ID",
                loadUrl: serviceUrl + "/GetAction",
                insertUrl: serviceUrl + "/InsertAction",
                updateUrl: serviceUrl + "/UpdateAction",
                deleteUrl: serviceUrl + "/DeleteAction"
            return {
                store
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import CustomStore from 'devextreme/data/custom_store';
    import { createStore } from 'devextreme-aspnet-data-nojquery';
    import DataGrid from 'devextreme-react/data-grid';
    const serviceUrl = "https://url/to/my/service";
    const store = createStore({
        key: "ID",
        loadUrl: serviceUrl + "/GetAction",
        insertUrl: serviceUrl + "/InsertAction",
        updateUrl: serviceUrl + "/UpdateAction",
        deleteUrl: serviceUrl + "/DeleteAction"
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    dataSource={store} />
    export default App;

    View Demo

  • Any other data source
    Implement a CustomStore . View Demo

  • Regardless of the data source on the input, the DataGrid always wraps it in the DataSource object. This object allows you to sort, filter, group, and perform other data shaping operations. To get its instance, call the getDataSource() method.

    Review the following notes about data binding:

  • Data field names cannot be equal to this and should not contain the following characters: . , : , [ , and ] .

  • If the DataGrid UI component gets data from a server, configure remoteOperations to notify the UI component about data operations the server performs.

  • Features like export and selection work incorrectly with mapped data objects . Use calculated columns instead of mapping.

  • jQuery
  • The stores are immutable. You cannot change their configurations at runtime. Instead, create a new store or DataSource and assign it to the dataSource property as shown in the following help topic: Get and Set Properties .
  • Angular
  • The stores are immutable. You cannot change their configurations at runtime. Instead, create a new store or DataSource and assign it to the dataSource property as shown in the following help topic: Two-Way Property Binding .
  • The stores are immutable. You cannot change their configurations at runtime. Instead, create a new store or DataSource and assign it to the dataSource property as shown in the following help topic: Two-Way Property Binding .
  • React
  • The stores are immutable. You cannot change their configurations at runtime. Instead, create a new store or DataSource and assign it to the dataSource property as shown in the following help topic: Controlled Mode .
  • The dataSource is empty or not set at design time. The dateSerializationFormat is needed, because the DataGrid cannot detect it automatically without a data source.

  • You use the createStore method from the DevExtreme.AspNet.Data extension and remote date-time values are specified in UTC. DevExtreme.AspNet.Data requires the dateSerializationFormat to correctly serialize these values.

  • Use one of the following values to specify the dateSerializationFormat property:

  • "yyyy-MM-dd" - local date

  • "yyyy-MM-ddTHH:mm:ss" - local date and time

  • "yyyy-MM-ddTHH:mm:ssZ" - UTC date and time

  • "yyyy-MM-ddTHH:mm:ssx" , "yyyy-MM-ddTHH:mm:ssxx" , "yyyy-MM-ddTHH:mm:ssxxx" - date and time with a timezone

  • This property applies only if the forceIsoDateParsing field is set to true in the global configuration object .

    See Also
  • Troubleshooting - Date String Handling Issues
  • columns[] . format
  • The UI component can allow a user to add, update and delete data. To control which of these operations are allowed, use the allowAdding , allowUpdating and allowDeleting properties. Editing can be carried out in different modes, which are detailed in the mode property's description.

    NOTE
    Before allowing a user to add, update, and delete, make sure that your data source supports these actions.
    [elementAttr]="{ id: 'elementId', class: 'class-name' }"> </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            :element-attr="dataGridAttributes">
        </DxDataGrid>
    </template>
    <script>
    import DxDataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        data() {
            return {
                dataGridAttributes: {
                    id: 'elementId',
                    class: 'class-name'
    </script>
    React
    App.js
    import React from 'react';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        dataGridAttributes = {
            id: 'elementId',
            class: 'class-name'
        render() {
            return (
                <DataGrid ...
                    elementAttr={this.dataGridAttributes}>
                </DataGrid>
    export default App;

    The error row displays data-related errors that may occur on the server during the UI component's runtime. Setting this property to false hides the error row, but the errors can still be viewed in the browser's console.

    See Also
  • onDataErrorOccured
  • Install or reference the required libraries

    Install the following libraries for the export:

  • Excel: The ExcelJS v4.4.0+ and FileSaver v2.0.2+ libraries. If you apply CSP rules , refer to the ExcelJS CSP Treats section for more information about potential vulnerabilities.

  • PDF: The jsPDF library.

  • jQuery
    <!-- Export to Excel -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.4.0/polyfill.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.4.0/exceljs.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.2/FileSaver.min.js"></script>
        <!-- Reference the DevExtreme sources here -->
    </head>
    <!-- Export to Pdf -->
        <!-- ... -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.0.0/jspdf.umd.min.js"></script>
        <!-- Reference the DevExtreme sources here -->
    </head>
    Angular
    Installation command
    tsconfig.app.json
    <!-- Export to Pdf  -->
    npm install jspdf
    <!-- Export to Excel -->
    npm install --save exceljs file-saver
    npm i --save-dev @types/file-saver
    <!-- Export to Excel -->
        "compilerOptions": {
            // ...
            "outDir": "./out-tsc/app",
            "types": ["node"]
    
    Installation command
    <!-- Export to Pdf  -->
    npm install jspdf
    <!-- Export to Excel -->
    npm install --save exceljs file-saver
    React
    Installation command
    <!-- Export to Pdf  -->
    npm install jspdf
    <!-- Export to Excel -->
    npm install --save exceljs file-saver
  • Enable the export UI
    Set the export . enabled property to true . This property enables export for all columns. Set a column's allowExporting property to false to prevent it from being exported:

    jQuery
    JavaScript
    $(function () {
        $("#dataGridContainer").dxDataGrid({
            export: {
                enabled: true
            columns: [{ ...
                allowExporting: false
                // ...
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ... >
        <dxo-export [enabled]="true"></dxo-export>
        <dxi-column ...
            [allowExporting]="false">
        </dxi-column>
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        // ...
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ... >
            <DxExport
                :enabled="true"
            <DxColumn ... 
                :allow-exporting="false"
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid, 
        DxExport,
        DxColumn
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxExport,
            DxColumn
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        Export,
        Column
    } from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ... >
                    <Export enabled={true} />
                    <Column ...
                        allowExporting={false}
                </DataGrid>
    export default App;
  • Export the DataGrid
    Implement the onExporting handler and call the excelExporter . exportDataGrid(options) or pdfExporter . exportDataGrid(options) method. In the code below, the exportDataGrid method exports the DataGrid as is. You can use ExcelExportDataGridProps / PdfExportDataGridProps to configure export settings. The DataGrid exports its data to an Excel worksheet or a PDF document. To save the Excel document, call the FileSaver's saveAs method. To save the PDF document, call the jsPDF's save method.

    The example below shows how to export DataGrid to Excel file.

    jQuery
    JavaScript
    $('#gridContainer').dxDataGrid({
        export: {
            enabled: true
        onExporting: function(e) { 
            var workbook = new ExcelJS.Workbook(); 
            var worksheet = workbook.addWorksheet('Main sheet'); 
            DevExpress.excelExporter.exportDataGrid({ 
                worksheet: worksheet, 
                component: e.component,
                customizeCell: function(options) {
                    options.excelCell.font = { name: 'Arial', size: 12 };
                    options.excelCell.alignment = { horizontal: 'left' };
            }).then(function() {
                workbook.xlsx.writeBuffer().then(function(buffer) { 
                    saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx'); 
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onExporting)="onExporting($event)">
        <dxo-export [enabled]="true"></dxo-export>
    </dx-data-grid>
    import { Component } from '@angular/core';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    import { Workbook } from 'exceljs';
    import { saveAs } from 'file-saver';
    import { ExportingEvent } from 'devextreme/ui/data_grid';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        onExporting(e: ExportingEvent) {
            const workbook = new Workbook();    
            const worksheet = workbook.addWorksheet('Main sheet');
            exportDataGrid({
                component: e.component,
                worksheet: worksheet,
                customizeCell: function(options) {
                    options.excelCell.font = { name: 'Arial', size: 12 };
                    options.excelCell.alignment = { horizontal: 'left' };
            }).then(function() {
                workbook.xlsx.writeBuffer()
                    .then(function(buffer: BlobPart) {
                        saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @exporting="onExporting">
            <DxExport
                :enabled="true"
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid, DxExport } from 'devextreme-vue/data-grid';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    import { Workbook } from 'exceljs';
    import saveAs from 'file-saver';
    export default {
        components: {
            DxDataGrid,
            DxExport
        methods: {
            onExporting(e) {
                const workbook = new Workbook();
                const worksheet = workbook.addWorksheet('Main sheet');
                exportDataGrid({
                    component: e.component,
                    worksheet: worksheet,
                    customizeCell: function(options) {
                        options.excelCell.font = { name: 'Arial', size: 12 };
                        options.excelCell.alignment = { horizontal: 'left' };
                }).then(function() {
                    workbook.xlsx.writeBuffer()
                        .then(function(buffer) {
                            saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { Workbook } from 'exceljs';
    import saveAs from 'file-saver';
    import DataGrid, { Export } from 'devextreme-react/data-grid';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    onExporting={this.onExporting}>
                    <Export enabled={true} />
                </DataGrid>
        onExporting(e) {
            const workbook = new Workbook();
            const worksheet = workbook.addWorksheet('Main sheet');
            exportDataGrid({
                component: e.component,
                worksheet: worksheet,
                customizeCell: function(options) {
                    options.excelCell.font = { name: 'Arial', size: 12 };
                    options.excelCell.alignment = { horizontal: 'left' };
            }).then(function() {
                workbook.xlsx.writeBuffer()
                    .then(function(buffer) {
                        saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
    export default App;

    The example below shows how to export DataGrid to PDF document.

    jQuery
    JavaScript
    $(function(){
        $('#exportButton').dxButton({
            // ...
            onClick: function() {
                const doc = new jsPDF();
                DevExpress.pdfExporter.exportDataGrid({
                    jsPDFDocument: doc,
                    component: dataGrid
                }).then(function() {
                    doc.save('Customers.pdf');
        const dataGrid = $('#gridContainer').dxDataGrid({
            // ...
        }).dxDataGrid('instance');
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-button ... 
        (onClick)="exportGrid($event)">
    </dx-button>
    <dx-data-grid ... >
        <!-- ... -->
    </dx-data-grid>
    import { Component } from '@angular/core';
    import { exportDataGrid as exportDataGridToPdf } from 'devextreme/pdf_exporter';
    import { jsPDF } from 'jspdf';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        @ViewChild(DxDataGridComponent, { static: false }) dataGrid: DxDataGridComponent;
        exportGrid() {
            const doc = new jsPDF();
            exportDataGridToPdf({
                jsPDFDocument: doc,
                component: this.dataGrid.instance
            }).then(() => {
                doc.save('Customers.pdf');
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule, DxButtonModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule,
            DxButtonModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
            <DxButton ...
                @click="exportGrid()"
            <DxDataGrid ...
                :ref="dataGridRef">
                <!-- ... -->
            </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    import DxButton from 'devextreme-vue/button';
    import { jsPDF } from 'jspdf';
    import { exportDataGrid as exportDataGridToPdf } from 'devextreme/pdf_exporter';
    const dataGridRef = 'dataGrid';
    export default {
        components: {
            DxDataGrid,
            DxButton
        data() {
            return {
                dataGridRef
        computed: {
            dataGrid: function() {
                return this.$refs[dataGridRef].instance;
        methods: {
            exportGrid() {
                const doc = new jsPDF();
                exportDataGridToPdf({
                    jsPDFDocument: doc,
                    component: this.dataGrid
                }).then(() => {
                    doc.save('Customers.pdf');
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    import Button from 'devextreme-react/button';
    import { jsPDF } from 'jspdf';
    import { exportDataGrid as exportDataGridToPdf } from 'devextreme/pdf_exporter';
    export default function App() {
        const dataGridRef = useRef(null);
        function exportGrid() {
            const doc = new jsPDF();
            const dataGrid = dataGridRef.current.instance();
            exportDataGridToPdf({
                jsPDFDocument: doc,
                component: dataGrid
            }).then(() => {
                doc.save('Customers.pdf');
        return (
            <React.Fragment>
                    <Button ...
                        onClick={exportGrid}
                    <DataGrid ...
                        ref={dataGridRef}
                        {/* ... */}
                    </DataGrid>
            </React.Fragment>
    

    The following restrictions apply when users export DataGrid:

  • Only XLSX and PDF files are supported out of the box. To export DataGrid to CSV, call the excelExporter.exportDataGrid(options) method as shown in the formats property example. Refer to the CSV Injection section to take the threat of a CSV Injection Attack into account.

  • Excel limits the number of grouping levels to 7, while in the DataGrid it is unlimited.

  • Only visible columns are exported. See the onExporting property description for a workaround.

  • Detail rows are not exported.

  • Group rows are always exported in an expanded state and the isExpanded property is ignored.

  • Customizations made in the cellTemplate, groupCellTemplate, headerCellTemplate, and dataRowTemplate are omitted, but you can recreate them in the exported file. For this purpose, use the excelExporter.customizeCell or pdfExporter.customizeCell function. Refer to the following demos for more information: Excel Cell Customization, PDF Cell Customization.

  • Data mapping is ignored. Use calculated columns instead.

  • Export to Excel Overview Demo Export to PDF Overview Demo Export Images to Excel Demo Export Images to PDF Demo

    See the FilterBuilder configuration for properties that you can specify in this object. Do not specify the fields array because the DataGrid automatically populates it to sync filter builder fields with grid columns.

    Angular
    NOTE
    The nested component that configures the filterBuilder property does not support event bindings and two-way property bindings.
    NOTE
    The nested component that configures the filterBuilder property does not support event bindings and two-way property bindings.
    Angular
    NOTE
    The nested component that configures the filterBuilderPopup property does not support event bindings and two-way property bindings.
    NOTE
    The nested component that configures the filterBuilderPopup property does not support event bindings and two-way property bindings.
    See Also
  • Filter Panel with Filter Builder
  • If you change the filter expression in the filter panel or filter builder, the changes are reflected in the filter row and header filter, and vice versa. You can disable this synchronization by setting the filterSyncEnabled property to false. In this case, the filter panel remains synchronized with the filter builder.

    NOTE
    If the filter panel is visible and at least one column includes headerFilter.dataSource or lookup.dataSource, disable the syncLookupFilterValues property. Otherwise, the filter panel may not display data correctly.
    See Also
  • Filter Panel with Filter Builder
  • filterBuilderPopup
  • To make the filter row visible, assign true to the filterRow.visible property.

    NOTE
    If you use a grouped data structure to display data in a lookup column, the All item in the filter row is not displayed.

    DataGrid Demo TreeList Demo

    See Also
  • Filter Row
  • filter(filterExpr)
  • clearFilter(filterName)
  • If filterSyncEnabled is true, the filter expression includes a combination of the filter row, header filter, and filter builder filters. Otherwise, it contains only the filter builder filter.

    Note that you should convert date strings into JavaScript Date objects before using them in the filter expression.

    The filter expression can contain the following operations: "=", "<>", "<", ">", "<=", ">=", "between", "contains", "notcontains", "startswith", "endswith", "anyof", "noneof", and the filter builder's custom operations. Use "anyof" and "noneof" to select and clear the selection of items in the header filter's popup menu. In the following code, "anyof" is used to select items with IDs 500 and 700:

    jQuery
    JavaScript
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            filterSyncEnabled: true,
            headerFilter: { visible: true },
            filterValue: ["ID", "anyof", [500, 700]], 
    
    Angular
    HTML
    TypeScript
    <dx-data-grid ...
        [filterSyncEnabled]="true"
        [filterValue]="['ID', 'anyof', [500, 700]]"> 
        <dxo-header-filter 
            [visible]="true">
        </dxo-header-filter>
    </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            :filter-sync-enabled="true"
            :filter-value="filterValue"> 
            <DxHeaderFilter :visible="true" />
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid, {
        DxHeaderFilter,
        // ...
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxHeaderFilter,
            // ...
        data() {
            return {
                filterValue: ['ID', 'anyof', [500, 700]]
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        HeaderFilter,
        // ...
    } from 'devextreme-react/data-grid';
    const filterValue = ['ID', 'anyof', [500, 700]];
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    filterSyncEnabled={true}
                    defaultFilterValue={filterValue}>
                    <HeaderFilter visible={true} />
                </DataGrid>
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        .FilterSyncEnabled(true)
        .HeaderFilter(hf => hf.Visible(true))
        .FilterValue("['ID', 'anyof', [500, 700]]")
    

    If a column's groupInterval property is set, the "anyof" and "noneof" operations for this column accept the beginning of intervals instead of exact values:

    jQuery
    JavaScript
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            headerFilter: { visible: true },
            filterValue: ["ID", "anyof", [500, 700]], // Filter intervals are 500-600 and 700-800
            columns: [{
                dataField: "ID",
                dataType: "number",
                headerFilter: { groupInterval: 100 }
            // ...
    
    Angular
    HTML
    TypeScript
    <dx-data-grid ...
        <!-- Filter intervals are 500-600 and 700-800 -->
        [(filterValue)]="['ID', 'anyof', [500, 700]]">
            <dxo-header-filter 
                [visible]="true">
            </dxo-header-filter>
            <dxi-column
                dataField="ID"
                dataType="number">
                    <dxo-header-filter 
                        [groupInterval]="100">
                    </dxo-header-filter>
            </dxi-column>
    </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            <!-- Filter intervals are 500-600 and 700-800 -->
            :filter-value="filterValue">
            <DxHeaderFilter :visible="true" />
            <DxColumn
                data-field="ID"
                data-type="number">
                <DxColumnHeaderFilter :group-interval="100" />
            </DxColumn>
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid, {
        DxColumn,
        DxHeaderFilter,
        DxColumnHeaderFilter,
        // ...
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxColumn,
            DxHeaderFilter,
            DxColumnHeaderFilter,
            // ...
        data() {
            return {
                filterValue: ['ID', 'anyof', [500, 700]]
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        Column,
        HeaderFilter,
        ColumnHeaderFilter,
        // ...
    } from 'devextreme-react/data-grid';
    const filterValue = ['ID', 'anyof', [500, 700]];
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    {/* Filter intervals are 500-600 and 700-800 */}
                    defaultFilterValue={filterValue}>
                    <HeaderFilter visible={true} />
                    <Column
                        dataField="ID"
                        dataType="number">
                        <ColumnHeaderFilter groupInterval={100} />
                    </Column>
                </DataGrid>
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        // Filter intervals are 500-600 and 700-800
        .HeaderFilter(headerFilter => headerFilter.Visible(true))
        .FilterValue("['ID', 'anyof', [500, 700]]")
        .Columns(columns => {
            columns.AddFor(m => m.ID)
                .DataType(GridColumnDataType.Number)
                .HeaderFilter(hf => hf.GroupInterval(100));
            // ...
    
    NOTE
    The DataSource does not support the "between", "anyof", "noneof", and custom operations. Use the getCombinedFilter(returnDataField) method to get the DataSource-compatible filter expression.
    See Also
  • Filter Panel with Filter Builder
  • Unlike banded columns, Band columns cannot be focused.

    The default index, -1, means that no column is focused.

    See Also
  • onFocusedCellChanging | onFocusedCellChanged
  • focusedRowIndex | focusedRowKey
  • Rows are initially sorted by keys if any field of remoteOperations is true.
  • The row with focusedRowIndex or focusedRowKey is highlighted.
  • When the data row area is focused, this row is focused and the area is scrolled down to it.
  • The onFocusedRowChanging and onFocusedRowChanged functions become active.
  • NOTE
    Specify the UI component's keyExpr or the Store's key property to ensure that the focused row feature works properly.

    DataGrid generates additional requests with comparison operators (for example, < and >) to calculate the page number where a row with a focused key is located. This logic does not work for certain key types (for example, GUID) and data providers (for example, ODataStore). You need to disable the autoNavigateToFocusedRow property or set remoteOperations to false to ensure it operates correctly.

    The focused row has a key and index on a page. When the pager is used for navigation, the focused row's index persists from page to page, but corresponds to a different row with a different key on each page.

    The default index of -1 indicates that no row is focused.

    The focusedRowKey takes precedence over the focusedRowIndex when both are specified.

    See Also
  • onFocusedRowChanging | onFocusedRowChanged
  • focusedColumnIndex
  • Column and Row Indexes
  • The focused row has a key and index on a page. When the pager is used for navigation, the focused row's index persists from page to page but corresponds to a different row with a different key on each page.

    In the DataGrid, group rows can also be focused. See the Group Index and Key topic for more information on how group keys are formed.

    Data in DataGrid can be grouped by one column or by several. Once a column is used for grouping, it is added to the group panel.

    By default, the group panel is hidden. To make it visible, set the groupPanel.visible property to true. Alternatively, the visibility of the group panel can depend on the device's screen size. To accomplish this behavior, set the visible property to "auto".

    In case you need to show the group panel, but make it irresponsive, assign false to the groupPanel.allowColumnDragging property. This is useful, for instance, when grid records are grouped initially and when the user needs to know about that grouping, but must not be able to change it.

    Record Grouping Demo Remote Grouping Demo

    See Also
  • Grouping
  • grouping.contextMenuEnabled - enables the user to group data using the context menu.
  • columns[].allowGrouping - disables group operations for an individual column.
  • A header filter allows a user to filter values in an individual column by including/excluding them in/from the applied filter. A click on a header filter icon invokes a popup menu with all unique values in the column. By selecting or clearing the selection of values in this menu, the user includes/excludes them in/from the filter.

    To make header filter icons visible, assign true to the headerFilter.visible property.

    A header filter's popup menu lists all column values. If they are numbers or dates, you can group them using the groupInterval property in the column's headerFilter. You can also provide a custom data source for a header filter using the dataSource property.

    The user's filtering preferences are saved in the filterValues property. The header filter's Select All checkbox changes the filterType property.

    DataGrid Demo TreeList Demo

    See Also
  • Header Filter
  • columns[].allowHeaderFiltering
  • filter(filterExpr)
  • clearFilter(filterName)
  • The height in pixels.

  • String
    A CSS-accepted measurement of height. For example, "55px" , "20vh" , "80%" , "inherit" .

  • Function (deprecated since v21.2)
    Refer to the W0017 warning description for information on how you can migrate to viewport units.

  • NOTE
    DataGrid does not support the transform: scale CSS rule. Specify height and width as a percentage instead.
    SignalR Service Demo

    You can change the following CSS rules and classes that control highlighting:

    @keyframes dx-datagrid-highlight-change { from { background-color: #efefef; 50% { background-color: #efefef; .dx-datagrid-cell-updated-animation { animation: dx-datagrid-highlight-change 1s; .dx-datagrid-row-inserted-animation { animation: dx-datagrid-highlight-change 1s;

    Since the load panel is, in fact, the DevExtreme LoadPanel UI component, the loadPanel object can contain any properties of this UI component along with properties described here.

    See Also
  • Load Panel
  • beginCustomLoading(messageText)
  • endCustomLoading()
  • In DataGrid, a master-detail interface supplies a usual data row with an expandable section that contains the details on this data row. In that case, the data row is called "master row", while the section is called "detail section".

    To enable the master-detail interface, assign true to the masterDetail . enabled property. After that, specify the template for detail sections using the masterDetail . template property. Templates allow you to place virtually anything into the detail sections. For example, you can display another DataGrid or any other UI component there. For more information on specifying the template for the detail sections, see the template property description.

    Master-Detail View Demo Advanced Master-Detail View Demo

    See Also
  • Master-Detail Interface
  • Adaptive detail rows display information from columns that were hidden when the UI component adapted to the screen or container size. Each adaptive detail row contains the Form UI component that you can customize within the onAdaptiveDetailRowPreparing function using the formOptions object. Refer to the Form Configuration section for details on properties of the Form UI component.

    The following Form properties cannot be specified using formOptions :

  • template
  • editorType
  • any event handler ( properties whose name starts with "on..." )
  • onAdaptiveDetailRowPreparing: function(e) { e.formOptions.colCount = 2; e.formOptions.colCountByScreen = { xs: 2 e.formOptions.labelLocation = 'left';
    Angular
    TypeScript
    HTML
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        onAdaptiveDetailRowPreparing(e) {
            e.formOptions.colCount = 2;
            e.formOptions.colCountByScreen = {
                xs: 2
            e.formOptions.labelLocation = 'left';
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    <dx-data-grid ...
        (onAdaptiveDetailRowPreparing)="onAdaptiveDetailRowPreparing($event)">
    </dx-data-grid>
    App.vue (Options API)
    App.vue (Composition API)
    <template>
        <DxDataGrid
            @adaptive-detail-row-preparing="onAdaptiveDetailRowPreparing"
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
            onAdaptiveDetailRowPreparing(e) {
                e.formOptions.colCount = 2;
                e.formOptions.colCountByScreen = {
                    xs: 2
                e.formOptions.labelLocation = 'left';
    </script>
    <template>
        <DxDataGrid
            @adaptive-detail-row-preparing="onAdaptiveDetailRowPreparing"
    </template>
    <script setup>
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-vue/data-grid';
    const onAdaptiveDetailRowPreparing = (e) => {
        e.formOptions.colCount = 2;
        e.formOptions.colCountByScreen = {
            xs: 2
        e.formOptions.labelLocation = 'left';
    </script>
    React
    App.js
    import { useCallback } from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    export default function App() {
        const onAdaptiveDetailRowPreparing = useCallback((e) => {
            e.formOptions.colCount = 2;
            e.formOptions.colCountByScreen = {
                xs: 2
            e.formOptions.labelLocation = 'left';
        }, []);
        return (
            <DataGrid
                onAdaptiveDetailRowPreparing={onAdaptiveDetailRowPreparing}
    
    See Also
  • columnHidingEnabled
  • columns[].hidingPriority
  • Adaptability
  • Customize Adaptive Detail Row
  • Allows you to track a variable and execute actions when it changes. Applies when repaintChangesOnly is true.
    This function has the following parameters:

  • getter(data): Function
    A function that returns the variable that should be tracked.

  • handler(newValue): Function
    A function called when this variable changes.

  • In the following code, the onCellPrepared function is used to change a ProductName's color depending on the Amount of sold products. You can paste this code in the Real-Time Updates demo and see how it works.

    jQuery
    JavaScript
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            repaintChangesOnly: true,
            onCellPrepared: function(e) {
                if(e.rowType === "data" && e.column.dataField === "ProductName") {
                    e.cellElement.css("color", e.data.Amount >= 10000 ? "green" : "red");
                    // Tracks the `Amount` data field
                    e.watch(function() {
                        return e.data.Amount;
                    }, function() {
                        e.cellElement.css("color", e.data.Amount >= 10000 ? "green" : "red");
    
    Angular
    TypeScript
    HTML
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        onCellPrepared(e) {
            if(e.rowType === "data" && e.column.dataField === "ProductName") {
                e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
                // Tracks the `Amount` data field
                e.watch(function() {
                    return e.data.Amount;
                }, function() {
                    e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    <dx-data-grid ...
        [repaintChangesOnly]="true"
        (onCellPrepared)="onCellPrepared($event)">
    </dx-data-grid>
    App.vue
    <template>
        <DxDataGrid
            :repaint-changes-only="true"
            @cell-prepared="onCellPrepared"
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
           onCellPrepared(e) {
                if(e.rowType === "data" && e.column.dataField === "ProductName") {
                    e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
                    // Tracks the `Amount` data field
                    e.watch(function() {
                        return e.data.Amount;
                    }, function() {
                        e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        // ...
        render() {
            return (
                <DataGrid
                     repaintChangesOnly={true}
                     onCellPrepared={this.onCellPrepared}
        onCellPrepared = (e) => {
            if(e.rowType === "data" && e.column.dataField === "ProductName") {
                e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
                // Tracks the `Amount` data field
                e.watch(function() {
                    return e.data.Amount;
                }, function() {
                    e.cellElement.style.color = e.data.Amount >= 10000 ? "green" : "red";
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        .ID("dataGridContainer")
        // ...
        .RepaintChangesOnly(true)
        .OnCellPrepared("dataGrid_cellPrepared_handler")
    <script>
        function dataGrid_cellPrepared_handler(e) {
            if (e.rowType === "data" && e.column.dataField === "ProductName") {
                e.cellElement.css("color", e.data.Amount >= 10000 ? "green" : "red");
                // Tracks the `Amount` data field
                e.watch(function() {
                    return e.data.Amount;
                }, function() {
                    e.cellElement.css("color", e.data.Amount >= 10000 ? "green" : "red");
    </script>

    In the following code, the onContextMenuPreparing function adds a custom item to the context menu invoked when a user right-clicks any column header:

    jQuery
    index.js
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            onContextMenuPreparing: function(e) { 
                if (e.target == "header") {
                    // e.items can be undefined
                    if (!e.items) e.items = [];
                    // Add a custom menu item
                    e.items.push({
                        text: "Log Column Caption",
                        onItemClick: function() {
                            console.log(e.column.caption);
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onContextMenuPreparing)="addMenuItems($event)">
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        addMenuItems(e) { 
            if (e.target == 'header') {
                // e.items can be undefined
                if (!e.items) e.items = [];
                // Add a custom menu item
                e.items.push({
                    text: 'Log Column Caption',
                    onItemClick: () => {
                        console.log(e.column.caption);
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @context-menu-preparing="addMenuItems">
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        data() {
            return {
                // ...
        methods: {
            addMenuItems(e) {
                if (e.target == 'header') {
                    // e.items can be undefined
                    if (!e.items) e.items = [];
                    // Add a custom menu item
                    e.items.push({
                        text: 'Log Column Caption',
                        onItemClick: () => {
                            console.log(e.column.caption);
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        addMenuItems(e) {
            if (e.target == 'header') {
                // e.items can be undefined
                if (!e.items) e.items = [];
                // Add a custom menu item
                e.items.push({
                    text: 'Log Column Caption',
                    onItemClick: () => {
                        console.log(e.column.caption);
        render() {
            return (
                <DataGrid ...
                    onContextMenuPreparing={this.addMenuItems}>
                </DataGrid>
    export default App;

    Allows you to change the editor. Accepts names of DevExtreme UI components only, for example, "dxTextBox".
    Import a new editor's module when DevExtreme modules are used.

  • Override the default editor's onValueChanged handler. For other default editor customizations, use editorOptions.

    jQuery
    index.js
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            onEditorPreparing: function(e) {
                if (e.dataField === "requiredDataField" && e.parentType === "dataRow") {
                    const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                    e.editorOptions.onValueChanged = function(args) { // Override the default handler
                        // ...
                        // Custom commands go here
                        // ...
                        // If you want to modify the editor value, call the setValue function:
                        // e.setValue(newValue);
                        // Otherwise, call the default handler:
                        defaultValueChangeHandler(args);
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onEditorPreparing)="overrideOnValueChanged($event)">
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        overrideOnValueChanged(e) {
            if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
                const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                e.editorOptions.onValueChanged = function (args) { // Override the default handler
                    // ...
                    // Custom commands go here
                    // ...
                    // If you want to modify the editor value, call the setValue function:
                    // e.setValue(newValue);
                    // Otherwise, call the default handler:
                    defaultValueChangeHandler(args);
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @editor-preparing="overrideOnValueChanged">
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        // ...
        methods: {
            overrideOnValueChanged(e) {
                if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
                    const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                    e.editorOptions.onValueChanged = function (args) { // Override the default handler
                        // ...
                        // Custom commands go here
                        // ...
                        // If you want to modify the editor value, call the setValue function:
                        // e.setValue(newValue);
                        // Otherwise, call the default handler:
                        defaultValueChangeHandler(args);
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        overrideOnValueChanged(e) {
            if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
                const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                e.editorOptions.onValueChanged = function (args) { // Override the default handler
                    // ...
                    // Custom commands go here
                    // ...
                    // If you want to modify the editor value, call the setValue function:
                    // e.setValue(newValue);
                    // Otherwise, call the default handler:
                    defaultValueChangeHandler(args);
        render() {
            return (
                <DataGrid ...
                    onEditorPreparing={this.overrideOnValueChanged}>
                </DataGrid>
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        .OnEditorPreparing("overrideOnValueChanged")
    <script type="text/javascript">
        function overrideOnValueChanged(e) {
            if (e.dataField === "requiredDataField" && e.parentType === "dataRow") {
                const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                e.editorOptions.onValueChanged = function(args) { // Override the default handler
                    // ...
                    // Custom commands go here
                    // ...
                    // If you want to modify the editor value, call the setValue function:
                    // e.setValue(newValue);
                    // Otherwise, call the default handler:
                    defaultValueChangeHandler(args);
    </script>
  • Customize editors used in the search panel, filter row, and selection column.
    Use the parentType function parameter to check if the editor that the function customizes belongs to one of these UI elements.

  • Dynamically change editor properties in the editing state.

  • Implement other customization cases.

  • We do not recommend that you use the onEditorPreparing function to specify an editor's default value. Use the onInitNewRow function instead.

  • This function has the highest priority over the other editing tools. The order of priority is as follows: onEditorPreparing > columns.formItem > editing.form.

  • You can use this function to adjust column properties before export. In the following code, the column.visible property's value is changed to export the hidden ID column to an Excel file.

    jQuery
    JavaScript
    $(function() {
        $('#gridContainer').dxDataGrid({
            // ...
            export: {
                enabled: true
            columns: [{
                dataField: 'ID',
                visible: false
                // ...
            onExporting: function(e) { 
                e.component.beginUpdate();
                e.component.columnOption('ID', 'visible', true);
                var workbook = new ExcelJS.Workbook(); 
                var worksheet = workbook.addWorksheet('Main sheet');
                DevExpress.excelExporter.exportDataGrid({
                    component: e.component,
                    worksheet: worksheet
                }).then(function() {
                    workbook.xlsx.writeBuffer().then(function(buffer) {
                        saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
                }).then(function() {
                    e.component.columnOption('ID', 'visible', false);
                    e.component.endUpdate();
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onExporting)="onExporting($event)">
        <dxo-export [enabled]="true"></dxo-export>
        <dxi-column dataField="ID" [visible]="false"></dxi-column>
    </dx-data-grid>
    import { Component } from '@angular/core';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    import { Workbook } from 'exceljs';
    import saveAs from 'file-saver';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        onExporting(e) {
            e.component.beginUpdate();
            e.component.columnOption('ID', 'visible', true);
            const workbook = new Workbook();
            const worksheet = workbook.addWorksheet('Employees');
            exportDataGrid({
                component: e.component,
                worksheet: worksheet
            }).then(function() {
                workbook.xlsx.writeBuffer().then(function(buffer: BlobPart) {
                    saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
            }).then(function() {
                e.component.columnOption('ID', 'visible', false);
                e.component.endUpdate();
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @exporting="onExporting">
            <DxExport :enabled="true" />
            <DxColumn data-field="ID" :visible="false" />
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid, DxExport, DxColumn } from 'devextreme-vue/data-grid';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    import { Workbook } from 'exceljs';
    import saveAs from 'file-saver';
    export default {
        components: {
            DxDataGrid,
            DxExport,
            DxColumn
        methods: {
            onExporting(e) {
                e.component.beginUpdate();
                e.component.columnOption('ID', 'visible', true);
                const workbook = new Workbook();
                const worksheet = workbook.addWorksheet('Employees');
                exportDataGrid({
                    component: e.component,
                    worksheet: worksheet
                }).then(function() {
                    workbook.xlsx.writeBuffer().then(function(buffer) {
                        saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
                }).then(function() {
                    e.component.columnOption('ID', 'visible', false);
                    e.component.endUpdate();
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { Workbook } from 'exceljs';
    import saveAs from 'file-saver';
    import DataGrid, { Export, Column } from 'devextreme-react/data-grid';
    import { exportDataGrid } from 'devextreme/excel_exporter';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    onExporting={this.onExporting}>
                    <Export enabled={true} />
                    <Column dataField="ID" visible={false} />
                </DataGrid>
        onExporting(e) {
            e.component.beginUpdate();
            e.component.columnOption('ID', 'visible', true);
            const workbook = new Workbook();
            const worksheet = workbook.addWorksheet('Employees');
            exportDataGrid({
                component: e.component,
                worksheet: worksheet
            }).then(function() {
                workbook.xlsx.writeBuffer().then(function(buffer) {
                    saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.xlsx');
            }).then(function() {
                e.component.columnOption('ID', 'visible', false);
                e.component.endUpdate();
    export default App;

    Export to Excel Overview Demo Export to PDF Overview Demo

    See Also
  • export
  • In the following code, the onFocusedCellChanging function is used to customize keyboard navigation within a row. The cell navigation is looped in a single row because focus moves to the row's first cell after reaching the last cell and vice versa:

    jQuery
    JavaScript
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            onFocusedCellChanging: function (e) {
                if (e.newColumnIndex == e.prevColumnIndex) {
                    e.newColumnIndex = (e.newColumnIndex == 0 ? e.columns.length - 1 : 0)
    
    Angular
    TypeScript
    HTML
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        onFocusedCellChanging (e) { 
            if (e.newColumnIndex == e.prevColumnIndex) {
                e.newColumnIndex = (e.newColumnIndex == 0 ? e.columns.length - 1 : 0)
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    <dx-data-grid ...
        (onFocusedCellChanging)="onFocusedCellChanging($event)">
    </dx-data-grid>
    App.vue
    <template>
        <DxDataGrid ...
            @focused-cell-changing="onFocusedCellChanging"
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
            onFocusedCellChanging(e) {
                if (e.newColumnIndex == e.prevColumnIndex) {
                    e.newColumnIndex = (e.newColumnIndex == 0 ? e.columns.length - 1 : 0);
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    onFocusedCellChanging={this.onFocusedCellChanging}
                </DataGrid>
        onFocusedCellChanging(e) { 
            if (e.newColumnIndex == e.prevColumnIndex) {
                e.newColumnIndex = (e.newColumnIndex == 0 ? e.columns.length - 1 : 0);
    export default App;
    See Also
  • focusedRowIndex | focusedRowKey
  • focusedColumnIndex
  • app.component.html
    app.component.ts
    <dx-data-grid ...
        (onInitialized)="saveInstance($event)">
    </dx-data-grid>
    import { Component } from "@angular/core";
    import DataGrid from "devextreme/ui/data_grid";
    // ...
    export class AppComponent {
        dataGridInstance: DataGrid;
        saveInstance (e) {
            this.dataGridInstance = e.component;
    
    App.vue (Options API)
    App.vue (Composition API)
    <template>
            <DxDataGrid ...
                @initialized="saveInstance">
            </DxDataGrid>
    </template>
    <script>
    import DxDataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        data: function() {
            return {
                dataGridInstance: null
        methods: {
            saveInstance: function(e) {
                this.dataGridInstance = e.component;
    </script>
    <template>
            <DxDataGrid ...
                @initialized="saveInstance">
            </DxDataGrid>
    </template>
    <script setup>
    import DxDataGrid from 'devextreme-vue/data-grid';
    let dataGridInstance = null;
    const saveInstance = (e) => {
        dataGridInstance = e.component;
    </script>
    React
    App.js
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.saveInstance = this.saveInstance.bind(this);
        saveInstance(e) {
            this.dataGridInstance = e.component;
        render() {
            return (
                    <DataGrid onInitialized={this.saveInstance} />
    
    See Also
    jQuery
  • Get a UI component Instance in jQuery
  • Angular
  • Get a UI component Instance in Angular
  • Get a UI component Instance in Vue
  • React
  • Get a UI component Instance in React
  • You can use this function to populate a new row with data. Add fields to the data object that correspond to the data source object's fields. Note that the data object can omit some fields from the data source object. Add only those fields that should initialize specific cells of a new row.

    DataGrid Demo TreeList Demo

    In the following code, the onInitNewRow function is used to provide default values for the new row's ID, hireDate, and position cells. The promise parameter is used to obtain values for the ID and position cell values asynchronously:

    jQuery
    index.js
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            dataSource: [{
                ID: 1,
                hireDate: 1491821760000,
                position: "CTO"
            }, // ...
            columns: [ "ID", {
                dataField: "hireDate",
                dataType: "date"
            }, "position" ],
            onInitNewRow: function(e) {
                e.data.hireDate = new Date();
                e.promise = getDefaultData().done(function(data) {
                    e.data.ID = data.ID;
                    e.data.position = data.Position;
        function getDefaultData() {
            var promise = $.ajax({
                // The URL returns { ID: 100, Position: "Programmer" }
                url: "https://www.mywebsite.com/api/getDefaultData", 
                dataType: "json"
            return promise;
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        [dataSource]="employees"
        (onInitNewRow)="onInitNewRow($event)">
        <dxi-column dataField="ID"></dxi-column>
        <dxi-column dataField="hireDate" dataType="date"></dxi-column>
        <dxi-column dataField="position"></dxi-column>
    </dx-data-grid>
    import { Component } from '@angular/core';
    import { lastValueFrom } from 'rxjs';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        employees = [{
            ID: 1,
            hireDate: 1491821760000,
            position: "CTO"
        }, // ...
        onInitNewRow(e) {
            e.data.hireDate = new Date();
            e.promise = this.getDefaultData().then((data: any) => {
                e.data.ID = data.ID;
                e.data.position = data.Position;
        getDefaultData() {
            return lastValueFrom(this.httpClient.get("https://www.mywebsite.com/api/getDefaultData"))
                .then(data => {
                    // "data" is { ID: 100, Position: "Programmer" }
                    return data;
                .catch(error => { throw 'Data Loading Error' });
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            :data-source="employees"
            @init-new-row="initNewRow">
            <DxColumn data-field="ID" />
            <DxColumn data-field="hireDate" data-type="date" />
            <DxColumn data-field="position" />
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid, DxColumn } from 'devextreme-vue/data-grid';
    import 'whatwg-fetch';
    const employees = [{
        ID: 1,
        hireDate: 1491821760000,
        position: "CTO"
    }, // ...
    export default {
        components: {
            DxDataGrid,
            DxColumn
        data() {
            employees
        methods: {
            initNewRow(e) {
                e.data.hireDate = new Date();
                e.promise = this.getDefaultData().then(data => {
                    e.data.ID = data.ID;
                    e.data.position = data.Position;
            getDefaultData() {
                return fetch("https://www.mywebsite.com/api/getDefaultData")
                    .then(response => response.json())
                    .then((data) => {
                        // "data" is { ID: 100, Position: "Programmer" }
                        return data;
                    .catch(() => { throw 'Data Loading Error' });
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { DataGrid, Column } from 'devextreme-react/data-grid';
    import 'whatwg-fetch';
    const employees = [{
        ID: 1,
        hireDate: 1491821760000,
        position: "CTO"
    }, // ...
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.onInitNewRow = this.onInitNewRow.bind(this);
            this.getDefaultData = this.getDefaultData.bind(this);
        onInitNewRow(e) {
            e.promise = this.getDefaultData().then(data => {
                e.data.ID = data.ID;
                e.data.position = data.Position;
            e.data.hireDate = new Date();
        getDefaultData() {
            return fetch("https://www.mywebsite.com/api/getDefaultData")
                .then(response => response.json())
                .then((data) => {
                    // "data" is { ID: 100, Position: "Programmer" }
                    return data;
                .catch(() => { throw 'Data Loading Error' });
        render() {
            return (
                <DataGrid ...
                    dataSource={employees}
                    onInitNewRow={this.onInitNewRow}>
                    <Column dataField="ID" />
                    <Column dataField="hireDate" dataType="date" />
                    <Column dataField="position" />
                </DataGrid>
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        .DataSource(new JS("employees"))
        .Columns(c => {
            c.Add().DataField("ID");
            c.Add().DataField("hireDate")
                .DataType(GridColumnDataType.Date);
            c.Add().DataField("position");
        .OnInitNewRow("onInitNewRow")
    <script type="text/javascript">
        var employees = [{
            ID: 1,
            hireDate: 1491821760000,
            position: "CTO"
        }, // ...
        function onInitNewRow(e) {
            e.data.hireDate = new Date();
            e.promise = getDefaultData().done(data => {
                e.data.ID = data.ID;
                e.data.position = data.Position;
        function getDefaultData() {
            let promise = $.ajax({
                // The URL returns { ID: 100, Position: "Programmer" }
                url: "https://www.mywebsite.com/api/getDefaultData",
                dataType: "json",
            return promise;
    </script>
    onKeyDown(e) { if (e.event.ctrlKey && e.event.key === "Q") { console.log("Ctrl + Q was pressed");
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onKeyDown)="onKeyDown($event)">
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        onKeyDown(e) {
            if (e.event.ctrlKey && e.event.key === "Q") {
                console.log("Ctrl + Q was pressed"); 
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        providers: [ ],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @key-down="onKeyDown">            
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
            onKeyDown(e) {
                if (e.event.ctrlKey && e.event.key === "Q") {
                    console.log("Ctrl + Q was pressed"); 
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    onKeyDown={this.onKeyDown}>
                </DataGrid>
        onKeyDown(e) {
            if (e.event.ctrlKey && e.event.key === "Q") {
                console.log("Ctrl + Q was pressed"); 
    export default App;
    onOptionChanged: function(e) { if(e.name === "changedProperty") { // handle the property change here
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onOptionChanged)="handlePropertyChange($event)"> 
    </dx-data-grid>
    import { Component } from '@angular/core'; 
    @Component({ 
        selector: 'app-root', 
        templateUrl: './app.component.html', 
        styleUrls: ['./app.component.css'] 
    export class AppComponent { 
        // ...
        handlePropertyChange(e) {
            if(e.name === "changedProperty") { 
                // handle the property change here
    import { BrowserModule } from '@angular/platform-browser'; 
    import { NgModule } from '@angular/core'; 
    import { AppComponent } from './app.component'; 
    import { DxDataGridModule } from 'devextreme-angular'; 
    @NgModule({ 
        declarations: [ 
            AppComponent 
        imports: [ 
            BrowserModule, 
            DxDataGridModule 
        providers: [ ], 
        bootstrap: [AppComponent] 
    export class AppModule { }  
    App.vue
    <template> 
        <DxDataGrid ...
            @option-changed="handlePropertyChange"
    </template> 
    <script>  
    import 'devextreme/dist/css/dx.light.css'; 
    import DxDataGrid from 'devextreme-vue/data-grid'; 
    export default { 
        components: { 
            DxDataGrid
        // ...
        methods: { 
            handlePropertyChange: function(e) {
                if(e.name === "changedProperty") {
                    // handle the property change here
    </script> 
    React
    App.js
    import React from 'react';  
    import 'devextreme/dist/css/dx.light.css'; 
    import DataGrid from 'devextreme-react/data-grid'; 
    const handlePropertyChange = (e) => {
        if(e.name === "changedProperty") {
            // handle the property change here
    export default function App() { 
        return ( 
            <DataGrid ...
                onOptionChanged={handlePropertyChange}
    

    The UI component executes the onCellClick function and can also execute internal functions before this function. Use the handled field to check whether internal functions were executed.

    In the following code, the onRowClick function calls the editRow method to switch the clicked row to the editing state. This functionality is best applied in form or popup editing.mode:

    jQuery
    index.js
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            editing: { mode: "form" },
            onRowClick: function(e) {
                if(e.rowType === "data") {
                    e.component.editRow(e.rowIndex);
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onRowClick)="startEdit($event)">
        <dxo-editing mode="form"></dxo-editing>
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        // ...
        startEdit(e) {
            if(e.rowType === "data") {
                e.component.editRow(e.rowIndex);
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            DxDataGridModule
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @row-click="startEdit">
            <DxEditing mode="form" />
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid, {
        DxEditing 
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
           startEdit(e) {
                if(e.rowType === "data") {
                    e.component.editRow(e.rowIndex);
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, {
        Editing 
    } from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid ...
                    onRowClick={this.startEdit}>
                    <Editing mode="form">
                </DataGrid>
        startEdit = (e) => {
            if(e.rowType === "data") {
                e.component.editRow(e.rowIndex);
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        .Editing(e => e.Mode(GridEditMode.Form))
        .OnRowClick("startEdit")
    <script type="text/javascript">
        function startEdit(e) {
            if(e.rowType === "data") {
                e.component.editRow(e.rowIndex);
    </script>
    NOTE
    The onRowClick function is not executed when the clicked row enters or is in the editing state. Instead, specify the onCellClick function.

    onRowDblClick is not executed when the clicked row enters or is in the editing state. You can use onCellDblClick instead.

    This event handler is also not executed on mobile devices, because double tap gesture is reserved for zooming. To force onRowDblClick execution, add the following CSS property to the UI component's container:

    <div style="touch-action:manipulation"></div>

    This function allows you to intercept row insertion and perform additional actions. The following code shows how to use the function parameter's cancel field to prevent or continue row insertion. In this code, a Promise is assigned to this field. Row insertion continues if a user confirms it and row data validation on the server succeeds (the Promise is resolved); otherwise, row insertion is prevented (the Promise is rejected):

    jQuery
    JavaScript
    $(function(){
        $("#dataGridContainer").dxDataGrid({
            // ...
            onRowInserting: function(e) {
                const deferred = $.Deferred();
                const promptPromise = DevExpress.ui.dialog.confirm("Are you sure?", "Confirm changes");
                promptPromise.done((dialogResult) => {
                    if (dialogResult) {
                        $.ajax({
                            url: "https://url/to/your/validation/service",
                            dataType: "json",
                            data: e.newData,
                            success: function(validationResult) {
                                if (validationResult.errorText) {
                                    deferred.reject(validationResult.errorText);
                                } else {
                                    deferred.resolve(false);
                            error: function() {
                                deferred.reject("Data Loading Error");
                            timeout: 5000
                    } else {
                        deferred.resolve(true);              
                e.cancel = deferred.promise();
    
    Angular
    app.component.ts
    app.component.html
    app.module.ts
    import { HttpClient, HttpClientModule, HttpParams } from "@angular/common/http";
    import { confirm } from 'devextreme/ui/dialog';
    import { lastValueFrom } from 'rxjs';
    // ...
    export class AppComponent {
        constructor(private httpClient: HttpClient) { /*...*/ }
        async insertRow(e) {
            try {
                const dialogResult = await this.confirmAsync("Are you sure?", "Confirm changes");
                if (dialogResult) {
                    let params = new HttpParams();
                    for (let key in e.newData) {
                        params = params.set(key, e.newData[key]);
                    const validationResult = await lastValueFrom(this.httpClient.get("https://url/to/your/validation/service", { params }));
                    if (validationResult.errorText) {
                        throw validationResult.errorText;
                    } else {
                        e.cancel = false;
                } else {
                    e.cancel = true;
            } catch (error) {
                console.error("Validation or confirmation error", error);
                e.cancel = Promise.reject(error);
        private confirmAsync(message: string, title?: string): Promise<boolean> {
            return new Promise<boolean>((resolve) => {
                const dialogResult = confirm(message, title);
                resolve(dialogResult);
    <dx-data-grid ... 
        (onRowInserting)="insertRow($event)">
    </dx-data-grid>
    // ... 
    import { DxDataGridModule } from 'devextreme-angular'; 
    import { HttpClientModule } from "@angular/common/http";
    @NgModule({
        imports: [
            // ...
            DxDataGridModule,
            HttpClientModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            @row-inserting="insertRow">
        </DxDataGrid>
    </template>
    <script>
    import DxDataGrid, { ... } from 'devextreme-vue/data-grid';
    import { confirm } from 'devextreme/ui/dialog';
    // ...
    export default {
        components: {
            DxDataGrid,
            // ...
        // ...
        methods: {
            insertRow(e) {
                const isCanceled = new Promise((resolve, reject) => {
                    const promptPromise = confirm("Are you sure?", "Confirm changes");
                    promptPromise.then((dialogResult) => {
                        if (dialogResult) {
                            let params = new HttpParams();
                            for (let key in e.newData) {
                                params = params.set(key, e.newData[key]);
                            fetch(`https://url/to/your/validation/service${params}`)
                                .then((validationResult) => {
                                    if (validationResult.errorText) {
                                        reject(validationResult.errorText);
                                    } else {
                                        resolve(false);
                        } else {
                            return resolve(true);
                e.cancel = isCanceled;
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { confirm } from 'devextreme/ui/dialog';
    import DataGrid, { ... } from 'devextreme-react/data-grid';
    function insertRow(e) {
        const isCanceled = new Promise((resolve, reject) => {
            const promptPromise = confirm("Are you sure?", "Confirm changes");
            promptPromise.then((dialogResult) => {
                if (dialogResult) {
                    let params = new HttpParams();
                    for (let key in e.newData) {
                        params = params.set(key, e.newData[key]);
                    fetch(`https://url/to/your/validation/service${params}`)
                        .then((validationResult) => {
                            if (validationResult.errorText) {
                                reject(validationResult.errorText);
                            } else {
                                resolve(false);
                } else {
                    return resolve(true);
        e.cancel = isCanceled;
    function App() {
        return (
            <DataGrid ...
                onRowInserting={insertRow}>
                // ...
            </DataGrid>
    export default App;
  • Do not use this function to insert data. If you need a custom insert logic, implement CustomStore's insert function.

  • In batch editing mode, this function is executed for each row individually if several rows should be inserted.

  • NOTE
    In batch editing mode, this function is executed for each row individually if several rows should be removed.

    This function allows you to intercept row removal and perform additional actions. The following code shows how to use the function parameter's cancel field to prevent or continue removal. In this code, a Promise is assigned to this field. Removal continues if a user confirms it and row validation on the server succeeds (the Promise is resolved); otherwise, removal is prevented (the Promise is rejected):

    jQuery
    JavaScript
    $(function(){
        $("#dataGridContainer").dxDataGrid({
            // ...
            onRowRemoving: function(e) {
                var deferred = $.Deferred();
                $.ajax({
                    url: `https://url/to/your/validation/service/${e.key}`,
                    success: function(validationResult) {
                        if (validationResult.errorText) {
                            deferred.reject(validationResult.errorText);
                        } else {
                            deferred.resolve(false);
                    error: function() {
                        deferred.reject("Data Loading Error");
                    timeout: 5000
                e.cancel = deferred.promise();
    
    Angular
    TypeScript
    app.component.html
    app.module.ts
    import { DxDataGridModule } from "devextreme-angular";
    import { HttpClient, HttpClientModule, HttpParams } from "@angular/common/http";
    import { lastValueFrom } from 'rxjs';
    export class AppComponent {
        constructor(private httpClient: HttpClient) { /*...*/ }
        validateRemove(e) {
            const isCanceled = new Promise((resolve, reject) => {
                const request$ = this.httpClient
                    .get(`https://url/to/your/validation/service/${e.key}`);
                lastValueFrom(request$).then((validationResult) => {
                    if (validationResult.errorText) {
                        reject(validationResult.errorText);
                    } else {
                        resolve(false);
            e.cancel = isCanceled;
    <dx-data-grid ... 
        (onRowRemoving)="validateRemove($event)">
    </dx-data-grid>
    // ... 
    import { DxDataGridModule } from 'devextreme-angular'; 
    import { HttpClientModule } from "@angular/common/http";
    @NgModule({
        imports: [
            // ...
            DxDataGridModule,
            HttpClientModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            @row-removing="validateRemove">
        </DxDataGrid>
    </template>
    <script>
    import DxDataGrid, { ... } from 'devextreme-vue/data-grid';
    // ...
    export default {
        components: {
            DxDataGrid,
            // ...
        // ...
        methods: {
            validateRemove(e) {
                const isCanceled = new Promise((resolve, reject) => {
                    fetch(`https://url/to/your/validation/service/${e.key}`)
                        .then((validationResult) => {
                            if (validationResult.errorText) {
                                reject(validationResult.errorText);
                            } else {
                                resolve(false);
                e.cancel = isCanceled;
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid, { ... } from 'devextreme-react/data-grid';
    function validateRemove(e) {
        const isCanceled = new Promise((resolve, reject) => {
            fetch(`https://url/to/your/validation/service/${e.key}`)
                .then((validationResult) => {
                    if (validationResult.errorText) {
                        reject(validationResult.errorText);
                    } else {
                        resolve(false);
        e.cancel = isCanceled;
    function App() {
        return (
            <DataGrid ...
                onRowRemoving={validateRemove}>
                // ...
            </DataGrid>
    export default App;

    This function allows you to intercept row update and perform additional actions. The following code shows how to use the function parameter's cancel field to prevent or continue row update. In this code, a Promise is assigned to this field. Row update continues if a user confirms it and row data validation on the server succeeds (the Promise is resolved); otherwise, row update is prevented (the Promise is rejected).

    jQuery
    JavaScript
    $(function(){
        $("#dataGridContainer").dxDataGrid({
            // ...
            onRowUpdating: function(e) {
                const deferred = $.Deferred();
                const promptPromise = DevExpress.ui.dialog.confirm("Are you sure?", "Confirm changes");
                promptPromise.done((dialogResult) => {
                    if (dialogResult) {
                        $.ajax({
                            url: "https://url/to/your/validation/service",
                            dataType: "json",
                            data: e.newData,
                            success: function(validationResult) {
                                if (validationResult.errorText) {
                                    deferred.reject(validationResult.errorText);
                                } else {
                                    deferred.resolve(false);
                            error: function() {
                                deferred.reject("Data Loading Error");
                            timeout: 5000
                    } else {
                        deferred.resolve(true);              
                e.cancel = deferred.promise();
    
    Angular
    app.component.ts
    app.component.html
    app.module.ts
    import { HttpClient, HttpClientModule, HttpParams } from "@angular/common/http";
    import { confirm } from 'devextreme/ui/dialog';
    import { lastValueFrom } from 'rxjs';
    // ...
    export class AppComponent {
        constructor(private httpClient: HttpClient) { /*...*/ }
        async updateRow(e) {
            try {
                const dialogResult = await this.confirmAsync("Are you sure?", "Confirm changes");
                if (dialogResult) {
                    let params = new HttpParams();
                    for (let key in e.newData) {
                        params = params.set(key, e.newData[key]);
                    const validationResult = await lastValueFrom(this.httpClient.get("https://url/to/your/validation/service", { params }));
                    if (validationResult.errorText) {
                        throw validationResult.errorText;
                    } else {
                        e.cancel = false;
                } else {
                    e.cancel = true;
            } catch (error) {
                console.error("Validation or confirmation error", error);
                e.cancel = Promise.reject(error);
        private confirmAsync(message: string, title?: string): Promise<boolean> {
            return new Promise<boolean>((resolve) => {
                const dialogResult = confirm(message, title);
                resolve(dialogResult);
    <dx-data-grid ... 
        (onRowUpdating)="updateRow($event)">
    </dx-data-grid>
    // ... 
    import { DxDataGridModule } from 'devextreme-angular'; 
    import { HttpClientModule } from "@angular/common/http";
    @NgModule({
        imports: [
            // ...
            DxDataGridModule,
            HttpClientModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid ...
            @row-updating="updateRow">
        </DxDataGrid>
    </template>
    <script>
    import DxDataGrid, { ... } from 'devextreme-vue/data-grid';
    import { confirm } from 'devextreme/ui/dialog';
    // ...
    export default {
        components: {
            DxDataGrid,
            // ...
        // ...
        methods: {
            updateRow(e) {
                const isCanceled = new Promise((resolve, reject) => {
                    const promptPromise = confirm("Are you sure?", "Confirm changes");
                    promptPromise.then((dialogResult) => {
                        if (dialogResult) {
                            let params = new HttpParams();
                            for (let key in e.newData) {
                                params = params.set(key, e.newData[key]);
                            fetch(`https://url/to/your/validation/service${params}`)
                                .then((validationResult) => {
                                    if (validationResult.errorText) {
                                        reject(validationResult.errorText);
                                    } else {
                                        resolve(false);
                        } else {
                            return resolve(true);
                e.cancel = isCanceled;
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { confirm } from 'devextreme/ui/dialog';
    import DataGrid, { ... } from 'devextreme-react/data-grid';
    function updateRow(e) {
        const isCanceled = new Promise((resolve, reject) => {
            const promptPromise = confirm("Are you sure?", "Confirm changes");
            promptPromise.then((dialogResult) => {
                if (dialogResult) {
                    let params = new HttpParams();
                    for (let key in e.newData) {
                        params = params.set(key, e.newData[key]);
                    fetch(`https://url/to/your/validation/service${params}`)
                        .then((validationResult) => {
                            if (validationResult.errorText) {
                                reject(validationResult.errorText);
                            } else {
                                resolve(false);
                } else {
                    return resolve(true);
        e.cancel = isCanceled;
    function App() {
        return (
            <DataGrid ...
                onRowUpdating={updateRow}>
                // ...
            </DataGrid>
    export default App;
  • You can use this function to change e.newData values, but do not use it to implement custom update logic. For this purpose, you can implement the onSaving or CustomStore's update function.

  • In batch editing mode, this function is executed for each row individually if several rows should be updated.

  • Use this function to perform operations before messages about failed validation are shown. For instance, you can run additional checks and change the isValid function parameter to change the validation result. You can also change the errorText parameter to correct the error message.

    The following code illustrates how to validate an email address on the server and display an error row with a custom error text if the validation fails:

    jQuery
    index.js
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            onRowValidating: function(e) {
                if(e.newData.Email) {
                    e.promise = checkEmail(e.newData.Email)
                        .done(function(result) {
                            e.errorText = result.errorText;
                            e.isValid = result.isValid;
    function checkEmail(email) {
        return $.ajax({
            // The url returns { errorText: "The Email address you entered already exists.", isValid: false }
            url: "https://www.mywebsite.com/api/checkEmail",
            dataType: "json",
            data: { email: email }
    
    Angular
    app.component.html
    app.component.ts
    app.module.ts
    <dx-data-grid ...
        (onRowValidating)="onRowValidating($event)">
    </dx-data-grid>
    import { Component } from '@angular/core';
    import { HttpClient, HttpParams } from '@angular/common/http';
    import { lastValueFrom } from 'rxjs';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        constructor(@Inject(HttpClient) http: HttpClient) {
            this.checkEmail = this.checkEmail.bind(this);
        onRowValidating(e) {
            if(e.newData.Email) {
                e.promise = this.checkEmail(e.newData.Email)
                    .then((result: any) => {
                        // "result" is { errorText: "The Email address you entered already exists.", isValid: false }
                        e.errorText = result.errorText;
                        e.isValid = result.isValid;
        checkEmail(email) {
            const params = new HttpParams().set("email", email);
            return lastValueFrom(
                this.http.get("https://www.mywebsite.com/api/checkEmail", { params })
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule, Component } from '@angular/core';
    import { HttpClientModule } from '@angular/common/http';
    import { AppComponent } from './app.component';
    import { DxDataGridModule } from 'devextreme-angular';
    @NgModule({
        declarations: [
            AppComponent
        imports: [
            BrowserModule,
            HttpClientModule,
            DxDataGridModule
        providers: [],
        bootstrap: [AppComponent]
    export class AppModule { }
    App.vue
    <template>
        <DxDataGrid ...
            @row-validating="onRowValidating">
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import DxDataGrid from 'devextreme-vue/data-grid';
    import 'whatwg-fetch';
    export default {
        components: {
            DxDataGrid
        // ...
        methods: {
            onRowValidating(e) {
                if(e.newData.Email) {
                    e.promise = this.checkEmail(e.newData.Email)
                        .then((result: any) => {
                            // "result" is { errorText: "The Email address you entered already exists.", isValid: false }
                            e.errorText = result.errorText;
                            e.isValid = result.isValid;
            checkEmail(email) {
                let params = '?' + 'email=' + email;
                return fetch("https://www.mywebsite.com/api/checkEmail${params}");
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import DataGrid from 'devextreme-react/data-grid';
    import 'whatwg-fetch';
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.onRowValidating = this.onRowValidating.bind(this);
        onRowValidating(e) {
            if(e.newData.Email) {
                e.promise = this.checkEmail(e.newData.Email)
                    .then((result: any) => {
                        // "result" is { errorText: "The Email address you entered already exists.", isValid: false }
                        e.errorText = result.errorText;
                        e.isValid = result.isValid;
        checkEmail(email) {
            let params = '?' + 'email=' + email;
            return fetch("https://www.mywebsite.com/api/checkEmail${params}");
        render() {
            return (
                <DataGrid ...
                    onRowValidating={this.onRowValidating}>
                </DataGrid>
    export default App;
    ASP.NET MVC Controls
    Razor C#
    @(Html.DevExtreme().DataGrid()
        // ...
        .OnRowValidating("onRowValidating")
    <script type="text/javascript">
        function onRowValidating(e) {
            if(e.newData.Email) {
                e.promise = checkEmail(e.newData.Email)
                    .done(function(result) {
                        e.errorText = result.errorText;
                        e.isValid = result.isValid;
        function checkEmail(email) {
            return $.ajax({
                // The url returns { errorText: "The Email address you entered already exists.", isValid: false }
                url: "https://www.mywebsite.com/api/checkEmail",
                dataType: "json",
                data: { email: email }
    </script>
    NOTE
    In batch editing mode, if changes in several rows are committed simultaneously, this function is executed for each row.
  • If a field providing key values is not specified in the data source, the whole data object is considered the key. In this case, all arrays passed to the function contain data objects instead of keys.
  • When selection is deferred, this function does not provide access to keys and data. Use the getSelectedRowsData() or getSelectedRowKeys() method instead.
  • If you use DevExtreme ASP.NET components or JQuery in your application, specify this property to get the component's instance. In Angular, Vue, or React, use the toolbar property instead.

    jQuery

    The following code adds a refresh button to the toolbar:

    $(function() {
        $("#container").dxDataGrid({
            // ...
            onToolbarPreparing: function(e) {
                let dataGrid = e.component;
                e.toolbarOptions.items.unshift({
                    location: "after",
                    widget: "dxButton",
                    options: {
                        icon: "refresh",
                        onClick: function() {
                            dataGrid.refresh();
    

    Paging allows the UI component to load data in portions instead of loading it simultaneously. To enable paging, set the paging.enabled property to true.

    Users can switch between pages and change paging settings using the pager or they can scroll the pages. Paging settings apply with any scrolling mode.

    DataGrid Demo TreeList Demo

    See Also
  • Paging
  • Server-side data processing improves the UI component's performance on large datasets. When the server does not implement particular operations (and/or the corresponding remoteOperations fields are false) they are executed on the client. Note that the UI component may send queries to the server while executing a client-side operation.

    The following table lists the possible remoteOperations configurations and the operations the server should implement. The server should also implement additional operations depending on the used UI component functionality.

    Setting Required server-side operations Additional server-side operations remoteOperations: true all operations except group paging remoteOperations: { groupPaging: true } all operations including group paging remoteOperations: { paging: true } paging filtering1, sorting1, summary calculation1 remoteOperations: { paging: true }
    (with grouping used in the UI component) paging, filtering, sorting grouping3, summary calculation1 remoteOperations: { filtering: true } filtering grouping4 remoteOperations: { sorting: true } sorting filtering1 remoteOperations: { grouping: true } grouping, filtering sorting1, summary calculation1 remoteOperations: { summary: true } summary calculation filtering1, sorting2, grouping2
  • If this functionality is used in the UI component.
  • If group summary calculation is used.
  • If grouping.autoExpandAll is set to false.
  • If DataGrid contains a lookup column and syncLookupFilterValues is set to true (default value).
  • NOTE
    Paging, filtering, and sorting are performed on the server side for the ODataStore, but you can change them to the client side by setting the corresponding remoteOperations fields to false. Other operations are always client-side.

    The following restrictions apply to UI component functionality when operations are remote:

  • Columns with the calculateCellValue or calculateDisplayValue property set cannot be sorted, filtered, or used for grouping.

  • The calculateGroupValue and calculateSortValue properties accept only string values.

  • Custom summary calculation is not supported.

  • The calculateFilterExpression property does not apply if it returns a function or a filter expression that contains functions.

  • Web API Service Demo Custom Service Demo

    See Also
  • Data Binding: Web API, PHP, MongoDB | Custom Sources
  • Follow these steps to migrate from the deprecated rowTemplate property to the dataRowTemplate property:

    jQuery
  • Rename the rowTemplate property to dataRowTemplate.

  • Remove <tbody>.

  • If <tbody> contained custom classes or attributes, use the onRowPrepared to add them to the dataRowTemplate.

  • If you implemented custom row alternation logic, you can remove it and set the rowAlternationEnabled property to true.

  • If you implemented custom hover logic, you can remove it. Instead, set the hoverStateEnabled property to true and use the dx-state-hover class to specify custom hover styles.

  • rowTemplate: function(container, item) { const { data } = item; const markup = "<tbody class='dx-row my-custom-class'>" + "<tr>" + "<td>" + item.data.id + "</td>" + "<td>" + item.data.name + "</td>" + "</tr>" + "</tbody>"; container.append(markup);

    After:

    index.js
    index.css
    $(function() {
        $("#dataGridContainer").dxDataGrid({
            // ...
            dataRowTemplate: function(container, item) {
                const { data } = item;
                const markup = "<tr>" +
                    "<td>" + item.data.id + "</td>" +
                    "<td>" + item.data.name + "</td>" +  
                "</tr>";
                container.append(markup);
            onRowPrepared: function({ rowElement }) {
                rowElement.addClass("my-custom-class");
    #dataGridContainer tbody.dx-state-hover {
        background-color: #ebebeb;
    
    Angular
  • Rename the rowTemplate property to dataRowTemplate.

  • Replace <tbody> with <ng-container>.

  • If <tbody> contained custom classes or attributes, use the onRowPrepared to add them to the dataRowTemplate.

  • If you implemented custom row alternation logic, you can remove it and set the rowAlternationEnabled property to true.

  • If you implemented custom hover logic, you can remove it. Instead, set the hoverStateEnabled property to true and use the dx-state-hover class to specify custom hover styles.

  • <dx-data-grid ... rowTemplate="rowTemplateName"> <tbody class="dx-row" *dxTemplate="let item of 'rowTemplateName'" > <td>{{item.data.id}}</td> <td>{{item.data.name}}</td> </tbody> </dx-data-grid>

    After:

    app.component.html
    app.component.ts
    app.component.css
    <dx-data-grid ...
        id="dataGridContainer"
        dataRowTemplate="dataRowTemplateName"
        (onRowPrepared)="addRowClasses">
        <ng-container *dxTemplate="let item of 'dataRowTemplateName'">
                <td>{{item.data.id}}</td>
                <td>{{item.data.name}}</td>
        </ng-container>
    </dx-data-grid>
    import { Component } from '@angular/core';
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    export class AppComponent {
        addRowClasses({ rowElement }) {
            rowElement.classList.add('my-custom-class');
    #dataGridContainer tbody.dx-state-hover {
        background-color: #ebebeb;
    
  • Rename the rowTemplate property to dataRowTemplate.

  • In Vue 3, remove <tbody>. In Vue 2, change <tbody> for a <div> with the following attribute: style="display: contents".

  • If <tbody> contained custom classes or attributes, use the onRowPrepared to add them to the dataRowTemplate.

  • If you implemented custom row alternation logic, you can remove it and set the rowAlternationEnabled property to true.

  • If you implemented custom hover logic, you can remove it. Instead, set the hoverStateEnabled property to true and use the dx-state-hover class to specify custom hover styles.

  • row-template="rowTemplate"> <!-- Vue 3 --> <template #rowTemplate="{ data: { data: { id, name } }"> <tbody class="dx-row"> <td>{{id}}</td> <td>{{name}}</td> </tbody> </template> <!-- Vue 2 --> <tbody slot="rowTemplate" slot-scope="{ data: { data: { id, name } } }" class="dx-row"> <td>{{id}}</td> <td>{{name}}</td> </tbody> </DxDataGrid> </template> <script> import { DxDataGrid } from 'devextreme-vue/data-grid'; export default { components: { DxDataGrid </script>

    After:

    App.vue
    <template>
        <DxDataGrid ...
            id="dataGridContainer"
            data-row-template="dataRowTemplate"
            @row-prepared="addRowClasses">
            <!-- Vue 3 -->
            <template #dataRowTemplate="{ data: { data: { id, name } }">
                    <td>{{id}}</td>
                    <td>{{name}}</td>
            </template>
            <!-- Vue 2 -->
            <div slot="dataRowTemplate"
                slot-scope="{ data: { data: { id, name } } }"
                style="display: contents">
                    <td>{{id}}</td>
                    <td>{{name}}</td>
        </DxDataGrid>
    </template>
    <script>
    import { DxDataGrid } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid
        methods: {
            addRowClasses({ rowElement }) {
                rowElement.classList.add('my-custom-class');
    </script>
    <style>
    #dataGridContainer tbody.dx-state-hover {
        background-color: #ebebeb;
    </style>
    React
  • Rename the rowRender property to dataRowRender.

  • Replace <tbody> with <React.Fragment>.

  • If <tbody> contained custom classes or attributes, use the onRowPrepared to add them to the dataRowTemplate.

  • If you implemented custom row alternation logic, you can remove it and set the rowAlternationEnabled property to true.

  • If you implemented custom hover logic, you can remove it. Instead, set the hoverStateEnabled property to true and use the dx-state-hover class to specify custom hover styles.

  • import DataGrid from 'devextreme-react/data-grid'; const Row = ({ data: { id, name } }) => { return ( <tbody className={"dx-row"}> <td>{id}</td> <td>{name}</td> </tbody> export default function App() { return ( <DataGrid ... rowRender={Row}> </DataGrid>

    After:

    App.js
    import DataGrid from 'devextreme-react/data-grid';
    import './App.css'
    const Row = ({ data: { id, name } }) => {
        return (
            <React.Fragment>
                    <td>{id}</td>
                    <td>{name}</td>
            </React.Fragment>
    const addRowClasses = ({ rowElement }) => {
        rowElement.classList.add('my-custom-class');
    export default function App() {
        return (
            <DataGrid ...
                id="dataGridContainer"
                dataRowRender={Row}
                onRowPrepared={addRowClasses}>
            </DataGrid>
    <!-- App.css -->
    #dataGridContainer tbody.dx-state-hover {
        background-color: #ebebeb;
    

    When this property is set to true, the UI component text flows from right to left, and the layout of elements is reversed. To switch the entire application/site to the right-to-left representation, assign true to the rtlEnabled field of the object passed to the DevExpress.config(config) method.

    JavaScript
    DevExpress.config({
        rtlEnabled: true
    

    DataGrid Demo Navigation UI Demo Editors Demo

    Scrolling allows a user to browse data left outside the current viewport. The UI component provides several scrolling modes detailed in the mode property description.

    Virtual Scrolling Demo Infinite Scrolling Demo

    See Also
  • Scrolling
  • pager
  • To make the search panel visible, set the searchPanel.visible property to true.

    DataGrid Demo TreeList Demo

    Keys are stored in the order the user selects rows.

    To access a row using its key, specify the data field that provides key values. Assign the data field's name to the key property of the store that underlies the dataSource.

    DataGrid Demo TreeList Demo

    See Also
  • Initial and Runtime Selection
  • getSelectedRowKeys()
  • getSelectedRowsData()
  • A user can select rows in a single or multiple mode. In multiple mode, a user can select all rows at once. To disable this feature, assign false to the allowSelectAll.

    Single Row Selection Demo Multiple Row Selection Demo

    By default, once a user selects a row, the data source is instantly notified about it. This may lower the UI component performance if the data source is remote and the user is allowed to select all rows at once. In this case, we recommend making the selection deferred.

    Deferred Selection Demo

    See Also
  • Selection
  • Deferred Selection
  • onSelectionChanged
  • NOTE
    If you use the Android or iOS theme, specifying this property doesn't affect anything. These themes avoid displaying column lines in order to provide a native look for the UI component. In case you still require the column lines to be displayed, choose another theme.
    See Also
  • showBorders
  • Normally, when records are grouped by a column, the groups are sorted according to the values of this column. In a number of cases, such approaches cannot address your needs, e.g., when you require to sort groups by the number of records in each. For these cases, you can implement sorting according to the values of group summary items. These items are specified in the groupItems array. Assume that you have the following code that specifies three group summary items.

    jQuery
    JavaScript
    $(function () {
        $("#dataGridContainer").dxDataGrid({
            // ...
            summary: {
                groupItems: [{
                    column: "Age",
                    summaryType: "avg",
                    name: "Average Age Group Summary"
                    column: "Income",
                    summaryType: "max"
                    column: "Tasks",
                    summaryType: "min"
    
    Angular
    HTML
    TypeScript
     <dx-data-grid ... >
         <dxo-summary>
             <dxi-group-item
                 column="Age"
                 summaryType="avg"
                 name="Average Age Group Summary">
             </dxi-group-item>
             <dxi-group-item
                 column="Income"
                 summaryType="max">
             </dxi-group-item>
             <dxi-group-item
                 column="Tasks"
                 summaryType="min">
             </dxi-group-item>
         </dxo-summary>
     </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid>
            <DxSummary>
                <DxGroupItem 
                    column="Age"
                    summary-type="avg"
                    name="Average Age Group Summary"
                <DxGroupItem
                    column="Income"
                    summary-type="max"
                <DxGroupItem
                    column="Tasks"
                    summary-type="min"
            </DxSummary>
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import { DxDataGrid, DxSummary, DxGroupItem } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxSummary,
            DxGroupItem
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { DataGrid, Summary, GroupItem } from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid>
                    <Summary>
                        <GroupItem 
                            column="Age"
                            summaryType="avg"
                            name="Average Age Group Summary" />
                        <GroupItem 
                            column="Income"
                            summaryType="max" />
                        <GroupItem 
                            column="Tasks" 
                            summaryType="min" />
                    </Summary>
                </DataGrid>
    export default App;

    To use these summary items for sorting groups, assign an array of objects to the sortByGroupSummaryInfo property. In each object of this array, specify the summaryItem field. This field determines the summary item to be used for summary-based sorting. In the following code, three objects form the sortByGroupSummaryInfo array. In each object, the summaryItem property determines different summary items using different values.

    jQuery
    JavaScript
    $(function () {
        $("#dataGridContainer").dxDataGrid({
            // ...
            sortByGroupSummaryInfo: [
                { summaryItem: 1 }, // determines the maximum income item using its index in the "groupItems" array
                { summaryItem: "min" }, // determines the minimum tasks item using its aggregate function
                { summaryItem: "Average Age Group Summary" } // determines the average age item using its name
    
    Angular
    HTML
    TypeScript
    <dx-data-grid ... >
        <dxi-sort-by-group-summary-info 
            [summaryItem]="1"> <!-- determines the maximum income item using its index in the "groupItems" array -->
        </dxi-sort-by-group-summary-info>
        <dxi-sort-by-group-summary-info 
            summaryItem="min"> <!-- determines the minimum tasks item using its aggregate function -->
        </dxi-sort-by-group-summary-info>
        <dxi-sort-by-group-summary-info 
            summaryItem="Average Age Group Summary"> <!-- determines the average age item using its name -->
        </dxi-sort-by-group-summary-info>
    </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid>
            <DxSortByGroupSummaryInfo
                :summary-item="1"/> <!-- determines the maximum income item using its index in the "groupItems" array -->
            <DxSortByGroupSummaryInfo
                summary-item="min"/> <!-- determines the minimum tasks item using its aggregate function -->
            <DxSortByGroupSummaryInfo
                summary-item="Average Age Group Summary"/> <!-- determines the average age item using its name -->
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import {
        DxDataGrid,
        DxSortByGroupSummaryInfo
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxSortByGroupSummaryInfo
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { DataGrid, SortByGroupSummaryInfo } from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid>
                    <SortByGroupSummaryInfo
                        summaryItem={1}/> {/* determines the maximum income item using its index in the "groupItems" array */}
                    <SortByGroupSummaryInfo
                        summaryItem="min"/> {/* determines the minimum tasks item using its aggregate function */}
                    <SortByGroupSummaryInfo
                        summaryItem="Average Age Group Summary"/> {/* determines the average age item using its name */}
                </DataGrid>
    export default App;

    After that, set the groupColumn property for objects in the sortByGroupSummaryInfo array. This property identifies the column that must be used in grouping in order that a particular summary-based sorting setting be applied. If you have omitted this property from an object, the sorting setting specified by this object will be applied regardless of the column used in grouping.

    jQuery
    JavaScript
    $(function () {
        $("#gridContainer").dxDataGrid({
            // ...
            sortByGroupSummaryInfo: [
                { summaryItem: 1, groupColumn: "Tasks" }, // applies sorting only when records are grouped by the "Tasks" column
                { summaryItem: "min", groupColumn: "Last Name" }, // applies sorting only when records are grouped by a "Last Name" column
                { summaryItem: "Average Age Group Summary" } // applies sorting regardless the grouping column
    
    Angular
    HTML
    TypeScript
    <dx-data-grid ... >
        <dxi-sort-by-group-summary-info 
            [summaryItem]="1" groupColumn="Tasks"> <!-- applies sorting only when records are grouped by the "Tasks" column -->
        </dxi-sort-by-group-summary-info>
        <dxi-sort-by-group-summary-info 
            summaryItem="min"
            groupColumn="Last Name"> <!-- applies sorting only when records are grouped by a "Last Name" column -->
        </dxi-sort-by-group-summary-info>
        <dxi-sort-by-group-summary-info 
            summaryItem="Average Age Group Summary"> <!--  applies sorting regardless the grouping column -->
        </dxi-sort-by-group-summary-info>
    </dx-data-grid>
    import { DxDataGridModule } from "devextreme-angular";
    // ...
    export class AppComponent {
        // ...
    @NgModule({
        imports: [
            // ...
            DxDataGridModule
        // ...
    
    App.vue
    <template>
        <DxDataGrid>
            <DxSortByGroupSummaryInfo
                :summary-item="1"
                group-column="Tasks"
            /> <!-- applies sorting only when records are grouped by the "Tasks" column -->
            <DxSortByGroupSummaryInfo
                summary-item="min"
                group-column="Last Name"
            /> <!-- applies sorting only when records are grouped by a "Last Name" column -->
            <DxSortByGroupSummaryInfo
                summary-item="Average Age Group Summary"
            /> <!-- applies sorting regardless the grouping column -->
        </DxDataGrid>
    </template>
    <script>
    import 'devextreme/dist/css/dx.light.css';
    import {
        DxDataGrid,
        DxSortByGroupSummaryInfo
    } from 'devextreme-vue/data-grid';
    export default {
        components: {
            DxDataGrid,
            DxSortByGroupSummaryInfo
    </script>
    React
    App.js
    import React from 'react';
    import 'devextreme/dist/css/dx.light.css';
    import { DataGrid, SortByGroupSummaryInfo } from 'devextreme-react/data-grid';
    class App extends React.Component {
        render() {
            return (
                <DataGrid>
                    <SortByGroupSummaryInfo
                        summaryItem={1}
                        groupColumn="Tasks"
                    /> {/* applies sorting only when records are grouped by the "Tasks" column */}
                    <SortByGroupSummaryInfo
                        summaryItem="min"
                        groupColumn="Last Name"
                    /> {/* applies sorting only when records are grouped by a "Last Name" column */}
                    <SortByGroupSummaryInfo
                        summaryItem="Average Age Group Summary"
                    /> {/* applies sorting regardless the grouping column */}
                </DataGrid>
    export default App;

    If several summary-based sorting settings match the current grouping, their indexes in the sortByGroupSummaryInfo array will dictate the order of their application.

    In addition, you can set an ascending or descending sort order for each summary-based sorting object using its sortOrder property.

    NOTE
    This feature does not work when remoteOperations.groupPaging is set to true.

    State storing enables the UI component to save applied settings and restore them the next time the UI component is loaded. Assign true to the stateStoring.enabled property to enable this functionality.

    State storing saves the following properties:

  • columns.sortIndex
  • columns.sortOrder
  • columns.visible (only if the column chooser is enabled)
  • columns.visibleIndex
  • columns.width
  • A summary is a grid feature that provides a synopsis of data contained in the grid. A summary consists of several items. A summary item displays a value that is a product of applying an aggregate function to the data of a specific column.

    There are two types of summary in DataGrid: group and total. The group summary is calculated on a group of data, which is segregated during grouping. To specify the items of the group summary, declare an array of objects and assign it to the summary.groupItems field.

    The total summary is calculated on all data contained in the grid. To specify the items of the total summary, declare an array of objects and assign it to the summary.totalItems field.

    Group Summaries Demo Total Summaries Demo

    The following table shows how the component behaves when you assign different values to this property. The 'State' column is filtered by the 'Alabama' value. If you set the syncLookupFilterValues to false, the 'City' column's header filter and filter row display all cities instead of showing cities within Alabama only.

    Filter type false Filter row
  • If filtering is enabled in remoteOperations and this property is set to true (default), the component uses the group parameter to fetch values. As a result, lookup columns can lose pagination (the DataSource.paginate property has no effect). To resolve this issue, disable this property.

  • If the filter panel is visible and at least one column includes headerFilter.dataSource or lookup.dataSource, disable this property. Otherwise, the filter panel may not display data correctly.

  • If the lookup column's data source lacks data that corresponds to the column data, the header filter may load incorrectly. To prevent this issue, you can either disable the syncLookupFilterValues property or the paginate option for the lookup column data source.

  • Two-way data binding ensures that the UI tracks changes made in the data source by a 3rd-party component, and vice versa. This way, the UI component and its data source stay synchronized.

    If you implement two-way data binding in the UI component on your own using the cellTemplate and/or editCellTemplate properties, make sure to set the twoWayBindingEnabled property to false.

    The width in pixels.

  • String
    A CSS-accepted measurement of width. For example, "55px", "20vw", "80%", "auto", "inherit".

  • Function (deprecated since v21.2)
    Refer to the W0017 warning description for information on how you can migrate to viewport units.

  • NOTE
    DataGrid does not support the transform: scale CSS rule. Specify height and width as a percentage instead.
    Feel free to share topic-related thoughts here.
    If you have technical questions, please create a support ticket in the DevExpress Support Center. All trademarks or registered trademarks are property of their respective owners. Use of this site constitutes acceptance of the Developer Express Inc Website Terms of Use, Privacy Policy (Updated), and . Use of DevExtreme UI components/libraries constitutes acceptance of the Developer Express Inc End User License Agreement. FAQs: Licensing | DevExpress Support Services | Supported Versions & Requirements | Maintenance Releases