DevExtreme jQuery - DataGrid and TreeList Issues

This section describes issues related to DataGrid and TreeList. If the solutions listed here do not help you resolve an issue, create a ticket in our Support Center and describe the issue in greater detail: Create a ticket.

Editors Values are not Saved

If an editor does not pass its value to the DataGrid or TreeList component and its DataSource when users enter or select a value, check if any of the following cases applies to your implementation.

A Column's dataField is Empty

Built-in column editors automatically write their values to the data row field specified in the column.dataField property. Specify this property if you want to write an editor's value to a field.

If you want to implement an unbound column, specify column.name instead of column.dataField and define the column.setCellValue callback to write values in a custom way.

jQuery
index.js
$('#gridContainer').dxDataGrid({
    // ...
    columns: [
        {   
            // ...
            setCellValue(newData, value, currentRowData) {
                newData.anyField = value;
            }
        }
    ]
});
Angular
app.component.html
app.component.ts
<dx-data-grid ... >
    <dxi-column ...
        [setCellValue]="setCellValue"
    >
    </dxi-column>
 </dx-data-grid>
export class AppComponent {
    export class AppComponent {
        setCellValue(newData, value, currentRowData) {
            newData.anyField = value;
        }
    }
}
Vue
App.vue
<template>
    <DxDataGrid ... >
        <DxColumn ...
            :set-cell-value="setCellValue"
        />
    </DxDataGrid>
</template>

<script>
    import { DxDataGrid, DxColumn } from 'devextreme-vue/data-grid';

    export default {
        components: {
            DxDataGrid,
            DxColumn
        },
        // ...
        methods: {
            setCellValue(newData, value, currentRowData) {
                newData.anyField = value;
            }
        }
    };
</script>
React
App.js
import React from 'react';
import DataGrid, { Column } from 'devextreme-react/data-grid';

const setCellValue = (newData, value, currentRowData) => {
    newData.anyField = value;
}

function App() {
    render (
        <React.Fragment>
            <DataGrid ... >
                <Column ...
                    setCellValue={setCellValue}
                />
            </DataGrid>
        </React.Fragment>
    );
}

export default App;

You Use a Form Item's Template to Declare an Editor

Form and popup edit modes use the built-in Form component. Do not use simpleItem.template to replace default editors in DataGrid or TreeList, use column.editCellTemplate instead.

You Implement an Editor in the editCellTemplate Body

Call the e.setValue method available in the template's argument. This method notifies the DataGrid or TreeList component that the value of a custom editor has changed. If you use a DevExtreme editor in this template, call e.setValue inside the onValueChanged event handler of this editor.

Customize Editors in DataGrid Demo

You Implement an Editor in the cellTemplate Body

DataGrid or TreeList uses column.cellTemplate only to display a cell value. To place your custom editor into cells to allow users to edit them, use column.editCellTemplate. Refer to the previous topic section for more information.

If you want to always display editors in a column, enable the column.showEditorAlways option. DataGrid displays an editor (or editCellTemplate if it exists) and ignores cellTemplate.

You Handle the editorPreparing Event and Override onValueChanged

Call the built-in onValueChanged event handler for an editor or call the e.setValue method available in the event's argument.

jQuery
index.js
$('#gridContainer').dxDataGrid({
    // ...
    onEditorPreparing(e) {
        if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
            const defaultValueChangeHandler = e.editorOptions.onValueChanged;
            e.editorOptions.onValueChanged = function (args) {
                e.setValue(args.value);
                // or
                defaultValueChangeHandler(args);
            }
        }
    }
});
Angular
app.component.html
app.component.ts
<dx-data-grid ... 
    (onEditorPreparing)="onEditorPreparing($event)"
>
</dx-data-grid>
export class AppComponent {
    export class AppComponent {
        onEditorPreparing(e) {
            if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
                const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                e.editorOptions.onValueChanged = function (args) {
                    e.setValue(args.value);
                    // or
                    defaultValueChangeHandler(args);
                }
            }
        }
    }
}
Vue
App.vue
<template>
    <DxDataGrid ... 
        @editor-preparing="onEditorPreparing"
    />
</template>

<script>
    import { DxDataGrid } from 'devextreme-vue/data-grid';

    export default {
        components: {
            DxDataGrid
        },
        // ...
        methods: {
            onEditorPreparing(e) {
                if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
                    const defaultValueChangeHandler = e.editorOptions.onValueChanged;
                    e.editorOptions.onValueChanged = function (args) {
                        e.setValue(args.value);
                        // or
                        defaultValueChangeHandler(args);
                    }
                }
            }
        }
    };
</script>
React
App.js
import React from 'react';
import DataGrid from 'devextreme-react/data-grid';

onEditorPreparing = (e) => {
    if (e.dataField === 'requiredDataField' && e.parentType === 'dataRow') {
        const defaultValueChangeHandler = e.editorOptions.onValueChanged;
        e.editorOptions.onValueChanged = function (args) {
            e.setValue(args.value);
            // or
            defaultValueChangeHandler(args);
        }
    }
}

function App() {
    render (
        <React.Fragment>
            <DataGrid ... 
                onEditorPreparing={onEditorPreparing}
            />
        </React.Fragment>
    );
}

export default App;
See Also

Gray Boxes Appear After Data Loading

In certain cases, gray boxes may remain visible after DataGrid or TreeList finishes loading data:

DataGrid with Gray Boxes Visible

These gray boxes are row placeholders that appear while the component loads and renders rows. These placeholders can remain visible in the following cases:

  • The component size does not match the container size.
  • An error occurs during data loading.
  • Loaded data is incorrect (has wrong structure, missing required fields, the number of loaded items is not expected, etc.)
  • Unexpected external interactions (when the component is re-rendering, the container is resized, the saved component state is corrupted, etc.)

Follow the steps below to troubleshoot your application. If the solutions listed here do not help you resolve an issue, create a ticket in our Support Center and describe the issue in greater detail: Create a ticket.

Use the Latest Version

jQuery

Update the devextreme package to the latest version for quick testing. The issue may be resolved in the most recent update. Do not forget to back up the existing project. If you upgrade between major versions, follow the instructions in the following topic: Migrate to the New Version.

Angular

Update devextreme and devextreme-angular packages to the latest version for quick testing. The issue may be resolved in the most recent update. Do not forget to back up the existing project. If you upgrade between major versions, follow the instructions in the following topic: Migrate to the New Version.

Vue

Update devextreme and devextreme-vue packages to the latest version for quick testing. The issue may be resolved in the most recent update. Do not forget to back up the existing project. If you upgrade between major versions, follow the instructions in the following topic: Migrate to the New Version.

React

Update devextreme and devextreme-react packages to the latest version for quick testing. The issue may be resolved in the most recent update. Do not forget to back up the existing project. If you upgrade between major versions, follow the instructions in the following topic: Migrate to the New Version.

Use Another Machine for Testing

If DataGrid or TreeList content is complex or the Internet connection is slow or unstable, gray boxes may be displayed for a longer time. If gray boxes take too long to disappear, you need to replace a remote data source with a local data source and open your project on a different machine.

Ensure Component Size Matches Container Size

The component displays placeholders based on its height property and the number of loaded rows. If the height property value does not match the component's container size, the component does not load enough rows to fill the available area. The remaining area is filled with gray boxes.

If no parent element of the DataGrid or TreeList component has a fixed height, the component cannot calculate its size correctly. If the height of component's parent is relative, make sure that the parent's ancestor has a fixed height. If multiple ancestors have relative heights, at least one of them should have a fixed height.

If you wish to fill the entire container area by height and, therefore, do not use fixed heights, set the height of all parents (including <html>) to 100%.

If you use the component inside ScrollView, set the height of DataGrid or TreeList to a fixed value.

The DataGrid or TreeList component does not track its container size. If the container size changes, call the repaint() or updateDimensions() method. The following guide describes this issue in greater detail: Components are Rendered Incorrectly if a Container State is Changed.

Ensure Component is Correctly Bound to Data Source

Check if an error occurs during data loading (see Check Console for Warnings or Errors). Then, check if the component loads correct data (see Check Requests in the Network Tab). If you expect raw data, an array of records is sufficient. If you enable remote operations, check the required data structure in the following topic: Server-Side Data Processing.

Some data shaping operations may make data inconsistent. For instance, in the following code snippet, the postProcess function returns the number of data rows that do not match the total count parameter. As a result, gray boxes appear:

jQuery
index.js
const dataSource = new DevExpress.data.DataSource({
    store: myStore,
    postProcess (data) {
        let filteredData = data.filter(condition);
        return filteredData;
    }
});
Angular
app.component.ts
import DataSource from "devextreme/data/data_source";
// ...
export class AppComponent {
    ds: DataSource;
    constructor() {
        this.ds = new DataSource({
            store: myStore,
            postProcess (data) {
                let filteredData = data.filter(condition);
                return filteredData;
            }
        });
    }
}
Vue
App.vue
<script>
import DataSource from 'devextreme/data/data_source';

const ds = new DataSource({
    store: myStore,
    postProcess (data) {
        let filteredData = data.filter(condition);
        return filteredData;
    }
});

export default {
    // ...
    data() {
        return {
            ds
        }
    }
}
</script>
React
App.js
// ...
import DataSource from 'devextreme/data/data_source';

const ds = new DataSource({
    store: myStore,
    postProcess (data) {
        let filteredData = data.filter(condition);
        return filteredData;
    }
});

function App() {
    // ...
}
export default App;

To resolve the issue, remove the postProcess function or change it to return an array with the same number of items as the total count parameter. For more information about data filtering, refer to the following article: DataGrid Filtering API.

Eliminate Side Effects

Follow the steps below to eliminate possible side effects:

  • Disable the stateStoring property. Check if the issue still persists. If the issue disappears, it is possible that the saved state is corrupted. Clear the saved state, enable stateStoring, and check again.

  • FireFox and Safari browsers raise native scrolling events asynchronously. This behavior forces asynchronous row rendering in native scrolling mode even when renderAsync is disabled. To avoid this side effect, disable the scrolling.useNative property. Call the defaultOptions(rule) method to resolve the issue for all DataGrid components in the application.

  • If you bind DataGrid or TreeList to one DataSource instance, reset the DataSource page index once you destroy the component. DataSource does not reset the index automatically. A new component bound to this DataSource may attempt to display the first data page while DataSource may contain only other data pages. The component displays gray boxes in such case. Call the DataSource.pageIndex(newIndex) method to reset the page index.

Angular
  • If you use conditional rendering to display DataGrid or TreeList, and virtual scrolling is enabled, the following happens:

    • on hide
      The scroll position is discarded.

    • on show
      The scroll position is set to the first page.

    If DataGrid or TreeList was opened, for example, on the page 5 before it was hidden, the gray boxes are displayed after the component is shown for the second time. This happens because the data source is loaded for the page 5 only.

    In such cases, save the scroll position on hide and restore it on show:

    app.component.ts
    export class AppComponent {
        // ...
        onContentVisibilityChange(visible) {
            if (!visible) {
                this.gridScrollPosition = this.dataGrid.instance.getScrollable().scrollTop();
            } else if (this.gridScrollPosition > 0) {
                this.dataGrid.instance.getScrollable().scrollTo({ top: this.gridScrollPosition }); 
        }
    }

    Alternatively, you can use CSS to hide the component. In this case, call the updateDimensions method.

Vue
  • If you use conditional rendering to display DataGrid or TreeList, and virtual scrolling is enabled, the following happens:

    • on hide
      The scroll position is discarded.

    • on show
      The scroll position is set to the first page.

    If DataGrid or TreeList was opened, for example, on page 5 before it was hidden, the gray boxes are displayed after the component is shown for the second time. This happens because the data source is loaded for page 5 only.

    In such cases, save the scroll position on hide and restore it on show:

    App.vue
    <script>
        export default {
            // ...
            methods: {
                onContentVisibilityChange(visible) {
                    if (!visible) {
                        this.gridScrollPosition = this.dataGrid.instance.getScrollable().scrollTop();
                    } else if (this.gridScrollPosition > 0) {
                        this.dataGrid.instance.getScrollable().scrollTo({ top: this.gridScrollPosition }); 
                    }
                }
            }
        }
    </script>

    Alternatively, you can use CSS to hide the component. In this case, call the updateDimensions method.

React
  • In certain cases, row placeholders appear due to redundant re-rendering. To learn more about such issues, refer to the following guide: Optimize Performance.

  • If you use conditional rendering to display DataGrid or TreeList, and virtual scrolling is enabled, the following happens:

    • on hide
      The scroll position is discarded.

    • on show
      The scroll position is set to the first page.

    If DataGrid or TreeList was opened, for example, on page 5 before it was hidden, the gray boxes are displayed after the component is shown for the second time. This happens because the data source is loaded for page 5 only.

    In such cases, save the scroll position on hide and restore it on show:

    App.js
    onContentVisibilityChange(visible) {
        if (!visible) {
            this.gridScrollPosition = this.dataGrid.instance.getScrollable().scrollTop();
        } else if (this.gridScrollPosition > 0) {
            this.dataGrid.instance.getScrollable().scrollTo({ top: this.gridScrollPosition }); 
        }
    }
    
    function App() {
        // ...
    }
    export default App;

    Alternatively, you can use CSS to hide the component. In this case, call the updateDimensions method.