External Form

The data of the KendoReact Grid can be edited by using an external form.

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 DialogContainer from './DialogContainer.jsx';
import cellWithEditing from './cellWithEditing.jsx';

class App extends React.Component {
    state = {
        products: sampleProducts.slice(0, 7),
        productInEdit: undefined
    };

    edit = (dataItem) => {
        this.setState({ productInEdit: this.cloneProduct(dataItem) });
    }

    remove = (dataItem) => {
        this.setState({
            products: this.state.products.filter(p => p.ProductID !== dataItem.ProductID)
        });
    }

    save = () => {
        const dataItem = this.state.productInEdit;
        const products = this.state.products.slice();
        const isNewProduct = dataItem.ProductID === undefined;

        if (isNewProduct) {
            products.unshift(this.newProduct(dataItem));
        } else {
            const index = products.findIndex(p => p.ProductID === dataItem.ProductID);
            products.splice(index, 1, dataItem);
        }

        this.setState({
            products: products,
            productInEdit: undefined
        });
    }

    cancel = () => {
        this.setState({ productInEdit: undefined });
    }

    insert = () => {
        this.setState({ productInEdit: { } });
    }

    render() {
        return (
            <div >
                <Grid
                    data={this.state.products}
                    style={{ height: '420px' }}
                >
                    <GridToolbar>
                        <button
                            onClick={this.insert}
                            className="k-button"
                        >
                            Add New
                        </button>
                    </GridToolbar>
                    <Column field="ProductID" title="Id" width="50px" />
                    <Column field="ProductName" title="Product Name" />
                    <Column field="UnitsInStock" title="Units In Stock" />
                    <Column field="Discontinued" />
                    <Column
                        title="Edit"
                        cell={cellWithEditing(this.edit, this.remove)}
                    />
                </Grid>
                {this.state.productInEdit && <DialogContainer dataItem={this.state.productInEdit} save={this.save} cancel={this.cancel}/>}
            </div>
        );
    }

    dialogTitle() {
        return `${this.state.productInEdit.ProductID === undefined ? 'Add' : 'Edit'} product`;
    }
    cloneProduct(product) {
        return Object.assign({}, product);
    }

    newProduct(source) {
        const id = this.state.products.reduce((acc, current) => Math.max(acc, current.ProductID || 0), 0) + 1;
        const newProduct = {
            ProductID: id,
            ProductName: '',
            UnitsInStock: 0,
            Discontinued: false
        };

        return Object.assign(newProduct, source);
    }
}

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

Setup

The following example utilizes the KendoReact Dialog as a modal form for editing the data of the Grid.

  1. When a record is in edit mode, show the container and pass the edited item to the DialogContainer component.

    {this.state.productInEdit &&
        <DialogContainer dataItem={this.state.productInEdit} save={this.save} cancel={this.cancel}/>}
  2. Inside DialogContainer, bind the editors to the value of the row data.

    <form onSubmit={this.handleSubmit}>
        <div style={{ marginBottom: '1rem' }}>
            <label>
            Product Name<br />
            <Input
                type="text"
                name="ProductName"
                value={this.state.productInEdit.ProductName || ''}
                onChange={this.onDialogInputChange}
            />
            </label>
        </div>
        <div style={{ marginBottom: '1rem' }}>
            <label>
            Units In Stock<br />
            <NumericTextBox
                name="UnitsInStock"
                value={this.state.productInEdit.UnitsInStock || 0}
                onChange={this.onDialogInputChange}
            />
            </label>
        </div>
        <div>
            <label>
            <input
                type="checkbox"
                name="Discontinued"
                checked={this.state.productInEdit.Discontinued || false}
                onChange={this.onDialogInputChange}
            />
            Discontinued product
                            </label>
        </div>
    </form>
  3. Handle the change events of the editors.

    onDialogInputChange = (event) => {
        let target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.props ? target.props.name : target.name;
    
        const edited = this.state.productInEdit;
        edited[name] = value;
    
        this.setState({
            productInEdit: edited
        });
    }
  4. To improve its performance, the Grid refreshes its data on each Update button click. To update the Grid on type, directly update the data state of the Grid on change.