Inline

The KendoReact Data Grid enables you to create, update, and delete data records inline.

The following example demonstrates how to implement the inline editing.

import React from 'react';
import ReactDOM from 'react-dom';
import { Grid, GridColumn as Column, GridToolbar } from '@progress/kendo-react-grid';

import { sampleProducts } from './sample-products.jsx';
import { MyCommandCell } from './myCommandCell.jsx';

class App extends React.Component {
    editField = "inEdit";
    CommandCell;

    state = {
        data: [ ...sampleProducts ]
    };

    constructor(props) {
        super(props);

        this.CommandCell = MyCommandCell({
            edit: this.enterEdit,
            remove: this.remove,

            add: this.add,
            discard: this.discard,

            update: this.update,
            cancel: this.cancel,

            editField: this.editField
        });
    }

    enterEdit = (dataItem) => {
        this.setState({
            data: this.state.data.map(item =>
                item.ProductID === dataItem.ProductID ?
                { ...item, inEdit: true } : item
            )
        });
    }

    remove = (dataItem) => {
        const data = [ ...this.state.data ];
        this.removeItem(data, dataItem);
        this.removeItem(sampleProducts, dataItem);

        this.setState({ data });
    }

    add = (dataItem) => {
        dataItem.inEdit = undefined;
        dataItem.ProductID = this.generateId(sampleProducts);

        sampleProducts.unshift(dataItem);
        this.setState({
            data: [ ...this.state.data ]
        });
    }

    discard = (dataItem) => {
        const data = [ ...this.state.data ];
        this.removeItem(data, dataItem);

        this.setState({ data });
    }

    update = (dataItem) => {
        const data = [ ...this.state.data ];
        const updatedItem = { ...dataItem, inEdit: undefined };

        this.updateItem(data, updatedItem);
        this.updateItem(sampleProducts, updatedItem);

        this.setState({ data });
    }

    cancel = (dataItem) => {
        const originalItem = sampleProducts.find(p => p.ProductID === dataItem.ProductID);
        const data = this.state.data.map(item => item.ProductID === originalItem.ProductID ? originalItem : item);

        this.setState({ data });
    }

    updateItem = (data, item) => {
        let index = data.findIndex(p => p === item || (item.ProductID && p.ProductID === item.ProductID));
        if (index >= 0) {
            data[index] = { ...item };
        }
    }

    itemChange = (event) => {
        const data = this.state.data.map(item =>
            item.ProductID === event.dataItem.ProductID ?
            { ...item, [event.field]: event.value } : item
        );

        this.setState({ data });
    }

    addNew = () => {
        const newDataItem = { inEdit: true, Discontinued: false };

        this.setState({
            data: [ newDataItem, ...this.state.data ]
        });
    }

    cancelCurrentChanges = () => {
        this.setState({ data: [ ...sampleProducts ] });
    }

    render() {
        const { data } = this.state;
        const hasEditedItem = data.some(p => p.inEdit);
        return (
            <Grid
                style={{ height: '420px' }}
                data={data}
                onItemChange={this.itemChange}
                editField={this.editField}
            >
                <GridToolbar>
                    <button
                        title="Add new"
                        className="k-button k-primary"
                        onClick={this.addNew}
                    >
                        Add new
                    </button>
                    {hasEditedItem && (
                        <button
                            title="Cancel current changes"
                            className="k-button"
                            onClick={this.cancelCurrentChanges}
                        >
                            Cancel current changes
                        </button>
                    )}
                </GridToolbar>
                <Column field="ProductID" title="Id" width="50px" editable={false} />
                <Column field="ProductName" title="Product Name" />
                <Column field="FirstOrderedOn" title="First Ordered" editor="date" format="{0:d}" />
                <Column field="UnitsInStock" title="Units" width="150px" editor="numeric" />
                <Column field="Discontinued" title="Discontinued" editor="boolean" />
                <Column cell={this.CommandCell} width="240px" />
            </Grid>
        );
    }

    generateId = data => data.reduce((acc, current) => Math.max(acc, current.ProductID), 0) + 1;

    removeItem(data, item) {
        let index = data.findIndex(p => p === item || item.ProductID && p.ProductID === item.ProductID);
        if (index >= 0) {
            data.splice(index, 1);
        }
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Setup

  1. Set the editField property of the Grid — editField will indicate the editable data items and is part of the temporary data items that are used during editing. For the data items in the edit mode, set the edit state in their editField.

    <Grid
        editField="inEdit"
    dataItem.inEdit = true;
  2. Configure the command column by defining the command buttons inside the GridCell component. In the example below, we use a Higher-Order Component function which receives all functions that will be executed from the command buttons and passes them to the command cell component.

    this.CommandCell = MyCommandCell({
        edit: this.enterEdit,
        remove: this.remove,
        add: this.add,
        discard: this.discard,
        update: this.update,
        cancel: this.cancel,
        editField: "inEdit"
    });
    import { GridCell } from '@progress/kendo-react-grid';
    
    export function MyCommandCell({ edit, remove, add, update, discard, cancel, editField }) {
        return class extends GridCell {
            render() {
                const { dataItem } = this.props;
                const inEdit = dataItem[editField];
                const isNewItem = dataItem.ProductID === undefined;
    
                return inEdit ? (
                    <td className="k-command-cell">
                        <button
                            className="k-button k-grid-save-command"
                            onClick={() => isNewItem ? add(dataItem) : update(dataItem)}
                        >
                            {isNewItem ? 'Add' : 'Update'}
                        </button>
                        <button
                            className="k-button k-grid-cancel-command"
                            onClick={() => isNewItem ? discard(dataItem) : cancel(dataItem)}
                        >
                            {isNewItem ? 'Discard' : 'Cancel'}
                        </button>
                    </td>
                ) : (
                    <td className="k-command-cell">
                        <button
                            className="k-primary k-button k-grid-edit-command"
                            onClick={() => edit(dataItem)}
                        >
                            Edit
                        </button>
                        <button
                            className="k-button k-grid-remove-command"
                            onClick={() => confirm('Confirm deleting: ' + dataItem.ProductName) &&
                                remove(dataItem)
                            }
                        >
                            Remove
                        </button>
                    </td>
                );
            }
        }
    };
  3. Define the edit, remove, add, update, discard and cancel functions needed by the command cell.

    edit = (dataItem) => {
        this.setState({
            data: this.state.data.map(item =>
                item.ProductID === dataItem.ProductID ?
                { ...item, inEdit: true } : item
            )
        });
    }
    remove = (dataItem) => {
        const data = [ ...this.state.data ];
        this.removeItem(data, dataItem);
        this.removeItem(sampleProducts, dataItem);
    
        this.setState({ data });
    }
    add = (dataItem) => {
        dataItem.inEdit = undefined;
        dataItem.ProductID = this.generateId(sampleProducts);
    
        sampleProducts.unshift(dataItem);
        this.setState({
            data: [ ...this.state.data ]
        });
    }
    discard = (dataItem) => {
        const data = [ ...this.state.data ];
        this.removeItem(data, dataItem);
    
        this.setState({ data });
    }
    update = (dataItem) => {
        const data = [ ...this.state.data ];
        const updatedItem = { ...dataItem, inEdit: undefined };
    
        this.updateItem(data, updatedItem);
        this.updateItem(sampleProducts, updatedItem);
    
        this.setState({ data });
    }
    
    updateItem = (data, item) => {
        let index = data.findIndex(p => p === item || (item.ProductID && p.ProductID === item.ProductID));
        if (index >= 0) {
            data[index] = { ...item };
        }
    }
    cancel = (dataItem) => {
        const originalItem = sampleProducts.find(p => p.ProductID === dataItem.ProductID);
        const data = this.state.data.map(item => item.ProductID === originalItem.ProductID ? originalItem : item);
    
        this.setState({ data });
    }
  4. Define a function for the onItemChange event which will handle any input changes during editing. Inside the event, all relevant data, such as the edited data item, the newly entered value, or the edited field, will be available as itemChange parameters.

    <Grid itemChange={this.itemChange}>
    itemChange = (event) => {
        const data = this.state.data.map(item =>
            item.ProductID === event.dataItem.ProductID ?
            { ...item, [event.field]: event.value } : item
        );
    
        this.setState({ data });
    }
  5. Per column, set the options that are related to editing:

  • editable — Determines if the column is editable.
  • editor — Specifies the data type of the column and, based on that, sets the appropriate editor.
In this article
 /