All Components

Inline

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

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 './my-command-cell.jsx';

class App extends React.Component {

    CommandCell;

    constructor(props) {
        super(props);

        this.state = {
            data: sampleProducts.slice(0)
        };

        this.enterInsert = this.enterInsert.bind(this);
        this.itemChange = this.itemChange.bind(this);

        const enterEdit = this.enterEdit.bind(this);
        const save = this.save.bind(this);
        const cancel = this.cancel.bind(this);
        const remove = this.remove.bind(this);
        this.CommandCell = MyCommandCell(enterEdit,remove,save,cancel, "inEdit");
    }

    enterInsert() {
        const dataItem = { inEdit: true, Discontinued: false };
        const newproducts = this.state.data.slice();
        newproducts.unshift(dataItem);
        this.update(newproducts, dataItem);
        this.setState({
            data: newproducts
        });
    }

    enterEdit(dataItem) {
        this.update(this.state.data, dataItem).inEdit = true;
        this.setState({
            data: this.state.data.slice()
        });
    }

    save(dataItem) {
        dataItem.inEdit = undefined;
        dataItem.ProductID = this.update(sampleProducts, dataItem).ProductID;
        this.setState({
            data: this.state.data.slice()
        });
    }

    cancel(dataItem) {
        if (dataItem.ProductID) {
            let originalItem = sampleProducts.find(p => p.ProductID === dataItem.ProductID);
            this.update(this.state.data, originalItem);
        } else {
            this.update(this.state.data, dataItem, !dataItem.ProductID);
        }
        this.setState({
            data: this.state.data.slice()
        });
    }

    remove(dataItem) {
        dataItem.inEdit = undefined;
        this.update(this.state.data, dataItem, true);
        this.update(sampleProducts, dataItem, true);
        this.setState({
            data: this.state.data.slice()
        });
    }

    itemChange(event) {
        const value = event.value;
        const name = event.field;
        if (!name) {
            return;
        }
        const updatedData = this.state.data.slice();
        const item = this.update(updatedData, event.dataItem);
        item[name] = value;
        this.setState({
            data: updatedData
        });
    }

    update(data, item, remove) {
        let updated;
        let index = data.findIndex(p => p === item || item.ProductID && p.ProductID === item.ProductID);
        if (index >= 0) {
            updated = Object.assign({}, item);
            data[index] = updated;
        } else {
            let id = 1;
            data.forEach(p => { id = Math.max(p.ProductID + 1, id); });
            updated = Object.assign({}, item, { ProductID: id });
            data.unshift(updated);
            index = 0;
        }

        if (remove) {
            data = data.splice(index, 1);
        }

        return data[index];
    }

    render() {
        return (
            <div>
                <Grid
                    style={{ maxHeight: '420px' }}
                    data={this.state.data}
                    onItemChange={this.itemChange}
                    editField="inEdit"
                >
                    <GridToolbar>
                        <button
                            title="Add new"
                            className="k-button k-primary"
                            onClick={this.enterInsert}
                        >Add new
                        </button>

                        {this.state.data.filter(p => p.inEdit).length > 0 && (
                            <button
                                title="Cancel current changes"
                                className="k-button"
                                onClick={(e) => this.setState({ data: sampleProducts.slice() })}
                            >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="180px" />
                </Grid>
            </div>
        );
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);
export const sampleProducts = [
    {
        "ProductID": 1,
        "ProductName": "Chai",
        "SupplierID": 1,
        "CategoryID": 1,
        "QuantityPerUnit": "10 boxes x 20 bags",
        "UnitPrice": 18,
        "UnitsInStock": 39,
        "UnitsOnOrder": 0,
        "ReorderLevel": 10,
        "Discontinued": false,
        "Category": {
            "CategoryID": 1,
            "CategoryName": "Beverages",
            "Description": "Soft drinks, coffees, teas, beers, and ales"
        },
        "FirstOrderedOn": new Date(1996, 8, 20)
    },
    {
        "ProductID": 2,
        "ProductName": "Chang",
        "SupplierID": 1,
        "CategoryID": 1,
        "QuantityPerUnit": "24 - 12 oz bottles",
        "UnitPrice": 19,
        "UnitsInStock": 17,
        "UnitsOnOrder": 40,
        "ReorderLevel": 25,
        "Discontinued": false,
        "Category": {
            "CategoryID": 1,
            "CategoryName": "Beverages",
            "Description": "Soft drinks, coffees, teas, beers, and ales"
        },
        "FirstOrderedOn": new Date(1996, 7, 12)
    },
    {
        "ProductID": 3,
        "ProductName": "Aniseed Syrup",
        "SupplierID": 1,
        "CategoryID": 2,
        "QuantityPerUnit": "12 - 550 ml bottles",
        "UnitPrice": 10,
        "UnitsInStock": 13,
        "UnitsOnOrder": 70,
        "ReorderLevel": 25,
        "Discontinued": false,
        "Category": {
            "CategoryID": 2,
            "CategoryName": "Condiments",
            "Description": "Sweet and savory sauces, relishes, spreads, and seasonings"
        },
        "FirstOrderedOn": new Date(1996, 8, 26)
    },
    {
        "ProductID": 4,
        "ProductName": "Chef Anton's Cajun Seasoning",
        "SupplierID": 2,
        "CategoryID": 2,
        "QuantityPerUnit": "48 - 6 oz jars",
        "UnitPrice": 22,
        "UnitsInStock": 53,
        "UnitsOnOrder": 0,
        "ReorderLevel": 0,
        "Discontinued": false,
        "Category": {
            "CategoryID": 2,
            "CategoryName": "Condiments",
            "Description": "Sweet and savory sauces, relishes, spreads, and seasonings"
        },
        "FirstOrderedOn": new Date(1996, 9, 19)
    },
    {
        "ProductID": 5,
        "ProductName": "Chef Anton's Gumbo Mix",
        "SupplierID": 2,
        "CategoryID": 2,
        "QuantityPerUnit": "36 boxes",
        "UnitPrice": 21.35,
        "UnitsInStock": 0,
        "UnitsOnOrder": 0,
        "ReorderLevel": 0,
        "Discontinued": true,
        "Category": {
            "CategoryID": 2,
            "CategoryName": "Condiments",
            "Description": "Sweet and savory sauces, relishes, spreads, and seasonings"
        },
        "FirstOrderedOn": new Date(1996, 7, 17)
    },
    {
        "ProductID": 6,
        "ProductName": "Grandma's Boysenberry Spread",
        "SupplierID": 3,
        "CategoryID": 2,
        "QuantityPerUnit": "12 - 8 oz jars",
        "UnitPrice": 25,
        "UnitsInStock": 120,
        "UnitsOnOrder": 0,
        "ReorderLevel": 25,
        "Discontinued": false,
        "Category": {
            "CategoryID": 2,
            "CategoryName": "Condiments",
            "Description": "Sweet and savory sauces, relishes, spreads, and seasonings"
        },
        "FirstOrderedOn": new Date(1996, 9, 19)
    },
    {
        "ProductID": 7,
        "ProductName": "Uncle Bob's Organic Dried Pears",
        "SupplierID": 3,
        "CategoryID": 7,
        "QuantityPerUnit": "12 - 1 lb pkgs.",
        "UnitPrice": 30,
        "UnitsInStock": 15,
        "UnitsOnOrder": 0,
        "ReorderLevel": 10,
        "Discontinued": false,
        "Category": {
            "CategoryID": 7,
            "CategoryName": "Produce",
            "Description": "Dried fruit and bean curd"
        },
        "FirstOrderedOn": new Date(1996, 7, 22)
    },
    {
        "ProductID": 8,
        "ProductName": "Northwoods Cranberry Sauce",
        "SupplierID": 3,
        "CategoryID": 2,
        "QuantityPerUnit": "12 - 12 oz jars",
        "UnitPrice": 40,
        "UnitsInStock": 6,
        "UnitsOnOrder": 0,
        "ReorderLevel": 0,
        "Discontinued": false,
        "Category": {
            "CategoryID": 2,
            "CategoryName": "Condiments",
            "Description": "Sweet and savory sauces, relishes, spreads, and seasonings"
        },
        "FirstOrderedOn": new Date(1996, 11, 1)
    },
    {
        "ProductID": 9,
        "ProductName": "Mishi Kobe Niku",
        "SupplierID": 4,
        "CategoryID": 6,
        "QuantityPerUnit": "18 - 500 g pkgs.",
        "UnitPrice": 97,
        "UnitsInStock": 29,
        "UnitsOnOrder": 0,
        "ReorderLevel": 0,
        "Discontinued": true,
        "Category": {
            "CategoryID": 6,
            "CategoryName": "Meat/Poultry",
            "Description": "Prepared meats"
        },
        "FirstOrderedOn": new Date(1997, 1, 21)
    },
    {
        "ProductID": 10,
        "ProductName": "Ikura",
        "SupplierID": 4,
        "CategoryID": 8,
        "QuantityPerUnit": "12 - 200 ml jars",
        "UnitPrice": 31,
        "UnitsInStock": 31,
        "UnitsOnOrder": 0,
        "ReorderLevel": 0,
        "Discontinued": false,
        "Category": {
            "CategoryID": 8,
            "CategoryName": "Seafood",
            "Description": "Seaweed and fish"
        },
        "FirstOrderedOn": new Date(1996, 8, 5)
    }
];
import React from 'react';
import { GridCell } from '@progress/kendo-react-grid';

export default function MyCommandCell(enterEdit, remove, save, cancel, editField) {
    return class extends GridCell {
        render() {
            return !this.props.dataItem[editField]
                ? (
                    <td>
                        <button
                            className="k-primary k-button k-grid-edit-command"
                            onClick={(e) => enterEdit(this.props.dataItem)}>
                            Edit
                        </button>
                        <button
                            className="k-button k-grid-remove-command"
                            onClick={(e) => confirm('Confirm deleting: ' + this.props.dataItem.ProductName) && remove(this.props.dataItem)}>
                            Remove
                        </button>
                    </td>
                )
                : (
                    <td>
                        <button
                            className="k-button k-grid-save-command"
                            onClick={(e) => save(this.props.dataItem)}>
                            {this.props.dataItem.ProductID
                                ? 'Update'
                                : 'Add'}
                        </button>
                        <button
                            className="k-button k-grid-cancel-command"
                            onClick={(e) => cancel(this.props.dataItem)}>{this.props.dataItem.ProductID
                                ? 'Cancel'
                                : 'Discard'}
                        </button>
                    </td>
                );
        }
    }
};
In this article