Template editing in React Grid component
28 Sep 202324 minutes to read
Inline or dialog template editing
The Dialog/Inline template editing provides an option to customize the default behavior of dialog editing. Using the dialog template, you can render your own editors by defining the editSettings.mode as Dialog/Normal and editSetting.template as a React component.
In some cases, you need to add the new field editors in the dialog which are not present in the column model. In that situation, the dialog template will help you to customize the default edit dialog.
In the following sample, grid enabled with dialog template editing.
import { setValue } from '@syncfusion/ej2-base';
import { ColumnDirective, ColumnsDirective, GridComponent } from '@syncfusion/ej2-react-grids';
import { Edit, Inject, Toolbar } from '@syncfusion/ej2-react-grids';
import * as React from 'react';
import { data } from './datasource';
import { DialogFormTemplate } from './template';
function App() {
let grid;
const dialogTemplate = (props) => {
return (<DialogFormTemplate {...props}/>);
};
const editOptions = {
allowAdding: true, allowDeleting: true, allowEditing: true, mode: 'Dialog',
template: dialogTemplate
};
const toolbarOptions = ['Add', 'Edit', 'Delete'];
const actionComplete = (args) => {
if (args.form) {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
/** Add Validation Rules */
args.form.ej2_instances[0].addRules('Freight', { max: 500 });
}
/** Set initial Focus */
if (args.requestType === 'beginEdit') {
args.form.elements.namedItem('CustomerID').focus();
}
else if (args.requestType === 'add') {
args.form.elements.namedItem('OrderID').focus();
}
}
};
const actionBegin = (args) => {
if (args.requestType === 'save' && args.form) {
/** cast string to integer value */
setValue('data.Freight', parseFloat(args.form.querySelector("#Freight").value), args);
}
};
return <GridComponent ref={g => grid = g} dataSource={data} actionComplete={actionComplete} actionBegin={actionBegin} editSettings={editOptions} toolbar={toolbarOptions} height={265}>
<ColumnsDirective>
<ColumnDirective field='OrderID' headerText='Order ID' width='100' textAlign="Right" isPrimaryKey={true}/>
<ColumnDirective field='CustomerID' headerText='Customer ID' width='120'/>
<ColumnDirective field='ShipCountry' headerText='Ship Country' width='150'/>
</ColumnsDirective>
<Inject services={[Edit, Toolbar]}/>
</GridComponent>;
}
;
export default App;
import { setValue } from '@syncfusion/ej2-base';
import { ColumnDirective, ColumnsDirective, DialogEditEventArgs, Grid, GridComponent } from '@syncfusion/ej2-react-grids';
import { Edit, EditSettingsModel, Inject, Toolbar, ToolbarItems } from '@syncfusion/ej2-react-grids';
import * as React from 'react';
import { data } from './datasource';
import { IOrderModel } from './orderModel';
import { DialogFormTemplate } from './template';
function App() {
let grid: Grid | null;
const dialogTemplate = (props: IOrderModel): any => {
return (<DialogFormTemplate {...props} />);
}
const editOptions: EditSettingsModel = {
allowAdding: true, allowDeleting: true, allowEditing: true, mode: 'Dialog',
template: dialogTemplate
};
const toolbarOptions: ToolbarItems[] = ['Add', 'Edit', 'Delete'];
const actionComplete = (args: DialogEditEventArgs): void => {
if (args.form) {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
/** Add Validation Rules */
args.form.ej2_instances[0].addRules('Freight', { max: 500 });
}
/** Set initial Focus */
if (args.requestType === 'beginEdit') {
(args.form.elements.namedItem('CustomerID') as HTMLInputElement).focus();
} else if (args.requestType === 'add') {
(args.form.elements.namedItem('OrderID') as HTMLInputElement).focus();
}
}
}
const actionBegin = (args: DialogEditEventArgs) => {
if (args.requestType === 'save' && args.form) {
/** cast string to integer value */
setValue('data.Freight',
parseFloat((args.form.querySelector("#Freight") as HTMLInputElement).value), args);
}
}
return <GridComponent ref={g => grid = g} dataSource={data} actionComplete={actionComplete}
actionBegin={actionBegin} editSettings={editOptions} toolbar={toolbarOptions}
height={265}>
<ColumnsDirective>
<ColumnDirective field='OrderID' headerText='Order ID' width='100' textAlign="Right" isPrimaryKey={true} />
<ColumnDirective field='CustomerID' headerText='Customer ID' width='120' />
<ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</GridComponent>
};
export default App;
export let data = [
{
OrderID: 10248, CustomerID: 'VINET', Role: 'Admin', EmployeeID: 5, OrderDate: new Date(8364186e5),
ShipName: 'Vins et alcools Chevalier', ShipCity: 'Reims', ShipAddress: '59 rue de l Abbaye',
ShipRegion: 'CJ', Mask: '1111', ShipPostalCode: '51100', ShipCountry: 'France', Freight: 32.38, Verified: !0
},
{
OrderID: 10249, CustomerID: 'TOMSP', Role: 'Employee', EmployeeID: 6, OrderDate: new Date(836505e6),
ShipName: 'Toms Spezialitäten', ShipCity: 'Münster', ShipAddress: 'Luisenstr. 48',
ShipRegion: 'CJ', Mask: '2222', ShipPostalCode: '44087', ShipCountry: 'Germany', Freight: 11.61, Verified: !1
},
{
OrderID: 10250, CustomerID: 'HANAR', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8367642e5),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', Mask: '3333', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 65.83, Verified: !0
},
{
OrderID: 10251, CustomerID: 'VICTE', Role: 'Manager', EmployeeID: 3, OrderDate: new Date(8367642e5),
ShipName: 'Victuailles en stock', ShipCity: 'Lyon', ShipAddress: '2, rue du Commerce',
ShipRegion: 'CJ', Mask: '4444', ShipPostalCode: '69004', ShipCountry: 'France', Freight: 41.34, Verified: !0
},
{
OrderID: 10252, CustomerID: 'SUPRD', Role: 'Manager', EmployeeID: 4, OrderDate: new Date(8368506e5),
ShipName: 'Suprêmes délices', ShipCity: 'Charleroi', ShipAddress: 'Boulevard Tirou, 255',
ShipRegion: 'CJ', Mask: '5555', ShipPostalCode: 'B-6000', ShipCountry: 'Belgium', Freight: 51.3, Verified: !0
},
{
OrderID: 10253, CustomerID: 'HANAR', Role: 'Admin', EmployeeID: 3, OrderDate: new Date(836937e6),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', Mask: '6666', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 58.17, Verified: !0
},
{
OrderID: 10254, CustomerID: 'CHOPS', Role: 'Employee', EmployeeID: 5, OrderDate: new Date(8370234e5),
ShipName: 'Chop-suey Chinese', ShipCity: 'Bern', ShipAddress: 'Hauptstr. 31',
ShipRegion: 'CJ', Mask: '7777', ShipPostalCode: '3012', ShipCountry: 'Switzerland', Freight: 22.98, Verified: !1
},
{
OrderID: 10255, CustomerID: 'RICSU', Role: 'Admin', EmployeeID: 9, OrderDate: new Date(8371098e5),
ShipName: 'Richter Supermarkt', ShipCity: 'Genève', ShipAddress: 'Starenweg 5',
ShipRegion: 'CJ', Mask: '8888', ShipPostalCode: '1204', ShipCountry: 'Switzerland', Freight: 148.33, Verified: !0
},
{
OrderID: 10256, CustomerID: 'WELLI', Role: 'Employee', EmployeeID: 3, OrderDate: new Date(837369e6),
ShipName: 'Wellington Importadora', ShipCity: 'Resende', ShipAddress: 'Rua do Mercado, 12',
ShipRegion: 'SP', Mask: '9999', ShipPostalCode: '08737-363', ShipCountry: 'Brazil', Freight: 13.97, Verified: !1
},
{
OrderID: 10257, CustomerID: 'HILAA', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8374554e5),
ShipName: 'HILARION-Abastos', ShipCity: 'San Cristóbal', ShipAddress: 'Carrera 22 con Ave. Carlos Soublette #8-35',
ShipRegion: 'Táchira', Mask: '1234', ShipPostalCode: '5022', ShipCountry: 'Venezuela', Freight: 81.91, Verified: !0
},
{
OrderID: 10258, CustomerID: 'ERNSH', Role: 'Manager', EmployeeID: 1, OrderDate: new Date(8375418e5),
ShipName: 'Ernst Handel', ShipCity: 'Graz', ShipAddress: 'Kirchgasse 6',
ShipRegion: 'CJ', Mask: '2345', ShipPostalCode: '8010', ShipCountry: 'Austria', Freight: 140.51, Verified: !0
},
{
OrderID: 10259, CustomerID: 'CENTC', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8376282e5),
ShipName: 'Centro comercial Moctezuma', ShipCity: 'México D.F.', ShipAddress: 'Sierras de Granada 9993',
ShipRegion: 'CJ', Mask: '3456', ShipPostalCode: '05022', ShipCountry: 'Mexico', Freight: 3.25, Verified: !1
},
{
OrderID: 10260, CustomerID: 'OTTIK', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Ottilies Käseladen', ShipCity: 'Köln', ShipAddress: 'Mehrheimerstr. 369',
ShipRegion: 'CJ', Mask: '4567', ShipPostalCode: '50739', ShipCountry: 'Germany', Freight: 55.09, Verified: !0
},
{
OrderID: 10261, CustomerID: 'QUEDE', Role: 'Manager', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Que Delícia', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua da Panificadora, 12',
ShipRegion: 'RJ', Mask: '5678', ShipPostalCode: '02389-673', ShipCountry: 'Brazil', Freight: 3.05, Verified: !1
},
{
OrderID: 10262, CustomerID: 'RATTC', Role: 'Employee', EmployeeID: 8, OrderDate: new Date(8379738e5),
ShipName: 'Rattlesnake Canyon Grocery', ShipCity: 'Albuquerque', ShipAddress: '2817 Milton Dr.',
ShipRegion: 'NM', Mask: '6789', ShipPostalCode: '87110', ShipCountry: 'USA', Freight: 48.29, Verified: !0
}
];
export let data: Object[] = [
{
OrderID: 10248, CustomerID: 'VINET', Role: 'Admin', EmployeeID: 5, OrderDate: new Date(8364186e5),
ShipName: 'Vins et alcools Chevalier', ShipCity: 'Reims', ShipAddress: '59 rue de l Abbaye',
ShipRegion: 'CJ', Mask: '1111', ShipPostalCode: '51100', ShipCountry: 'France', Freight: 32.38, Verified: !0
},
{
OrderID: 10249, CustomerID: 'TOMSP', Role: 'Employee', EmployeeID: 6, OrderDate: new Date(836505e6),
ShipName: 'Toms Spezialitäten', ShipCity: 'Münster', ShipAddress: 'Luisenstr. 48',
ShipRegion: 'CJ', Mask: '2222', ShipPostalCode: '44087', ShipCountry: 'Germany', Freight: 11.61, Verified: !1
},
{
OrderID: 10250, CustomerID: 'HANAR', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8367642e5),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', Mask: '3333', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 65.83, Verified: !0
},
{
OrderID: 10251, CustomerID: 'VICTE', Role: 'Manager', EmployeeID: 3, OrderDate: new Date(8367642e5),
ShipName: 'Victuailles en stock', ShipCity: 'Lyon', ShipAddress: '2, rue du Commerce',
ShipRegion: 'CJ', Mask: '4444', ShipPostalCode: '69004', ShipCountry: 'France', Freight: 41.34, Verified: !0
},
{
OrderID: 10252, CustomerID: 'SUPRD', Role: 'Manager', EmployeeID: 4, OrderDate: new Date(8368506e5),
ShipName: 'Suprêmes délices', ShipCity: 'Charleroi', ShipAddress: 'Boulevard Tirou, 255',
ShipRegion: 'CJ', Mask: '5555', ShipPostalCode: 'B-6000', ShipCountry: 'Belgium', Freight: 51.3, Verified: !0
},
{
OrderID: 10253, CustomerID: 'HANAR', Role: 'Admin', EmployeeID: 3, OrderDate: new Date(836937e6),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', Mask: '6666', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 58.17, Verified: !0
},
{
OrderID: 10254, CustomerID: 'CHOPS', Role: 'Employee', EmployeeID: 5, OrderDate: new Date(8370234e5),
ShipName: 'Chop-suey Chinese', ShipCity: 'Bern', ShipAddress: 'Hauptstr. 31',
ShipRegion: 'CJ', Mask: '7777', ShipPostalCode: '3012', ShipCountry: 'Switzerland', Freight: 22.98, Verified: !1
},
{
OrderID: 10255, CustomerID: 'RICSU', Role: 'Admin', EmployeeID: 9, OrderDate: new Date(8371098e5),
ShipName: 'Richter Supermarkt', ShipCity: 'Genève', ShipAddress: 'Starenweg 5',
ShipRegion: 'CJ', Mask: '8888', ShipPostalCode: '1204', ShipCountry: 'Switzerland', Freight: 148.33, Verified: !0
},
{
OrderID: 10256, CustomerID: 'WELLI', Role: 'Employee', EmployeeID: 3, OrderDate: new Date(837369e6),
ShipName: 'Wellington Importadora', ShipCity: 'Resende', ShipAddress: 'Rua do Mercado, 12',
ShipRegion: 'SP', Mask: '9999', ShipPostalCode: '08737-363', ShipCountry: 'Brazil', Freight: 13.97, Verified: !1
},
{
OrderID: 10257, CustomerID: 'HILAA', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8374554e5),
ShipName: 'HILARION-Abastos', ShipCity: 'San Cristóbal', ShipAddress: 'Carrera 22 con Ave. Carlos Soublette #8-35',
ShipRegion: 'Táchira', Mask: '1234', ShipPostalCode: '5022', ShipCountry: 'Venezuela', Freight: 81.91, Verified: !0
},
{
OrderID: 10258, CustomerID: 'ERNSH', Role: 'Manager', EmployeeID: 1, OrderDate: new Date(8375418e5),
ShipName: 'Ernst Handel', ShipCity: 'Graz', ShipAddress: 'Kirchgasse 6',
ShipRegion: 'CJ', Mask: '2345', ShipPostalCode: '8010', ShipCountry: 'Austria', Freight: 140.51, Verified: !0
},
{
OrderID: 10259, CustomerID: 'CENTC', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8376282e5),
ShipName: 'Centro comercial Moctezuma', ShipCity: 'México D.F.', ShipAddress: 'Sierras de Granada 9993',
ShipRegion: 'CJ', Mask: '3456', ShipPostalCode: '05022', ShipCountry: 'Mexico', Freight: 3.25, Verified: !1
},
{
OrderID: 10260, CustomerID: 'OTTIK', Role: 'Admin', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Ottilies Käseladen', ShipCity: 'Köln', ShipAddress: 'Mehrheimerstr. 369',
ShipRegion: 'CJ', Mask: '4567', ShipPostalCode: '50739', ShipCountry: 'Germany', Freight: 55.09, Verified: !0
},
{
OrderID: 10261, CustomerID: 'QUEDE', Role: 'Manager', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Que Delícia', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua da Panificadora, 12',
ShipRegion: 'RJ', Mask: '5678', ShipPostalCode: '02389-673', ShipCountry: 'Brazil', Freight: 3.05, Verified: !1
},
{
OrderID: 10262, CustomerID: 'RATTC', Role: 'Employee', EmployeeID: 8, OrderDate: new Date(8379738e5),
ShipName: 'Rattlesnake Canyon Grocery', ShipCity: 'Albuquerque', ShipAddress: '2817 Milton Dr.',
ShipRegion: 'NM', Mask: '6789', ShipPostalCode: '87110', ShipCountry: 'USA', Freight: 48.29, Verified: !0
}];
export {};
export interface IOrderModel {
OrderID?: number;
CustomerID?: string;
Freight?: number;
OrderDate?: Date;
ShipCity?: string;
ShipCountry?: string;
ShipAddress?: string;
isAdd?: boolean;
Verified?: boolean;
}
import { DataUtil } from '@syncfusion/ej2-data';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { NumericTextBoxComponent } from '@syncfusion/ej2-react-inputs';
import * as React from 'react';
import { data as orderData } from './datasource';
export class DialogFormTemplate extends React.Component {
shipCountryDistinctData = DataUtil.distinct(orderData, 'ShipCountry', true);
constructor(props) {
super(props);
this.state = Object.assign({}, props);
}
onChange(args) {
this.setState({ [args.target.name]: args.target.value });
}
render() {
this.onChange = this.onChange.bind(this);
const data = this.state;
return (<div>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" type="text" disabled={!data.isAdd} value={data.OrderID} onChange={this.onChange}/>
<span className="e-float-line"/>
<label className="e-float-text e-label-top"> Order ID</label>
</div>
</div>
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input value={data.CustomerID} id="CustomerID" name="CustomerID" type="text" onChange={this.onChange}/>
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Customer Name</label>
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<NumericTextBoxComponent id="Freight" format='C2' value={data.Freight} placeholder="Freight" floatLabelType='Always'/>
</div>
<div className="form-group col-md-6">
<DropDownListComponent id="ShipCountry" value={data.ShipCountry} dataSource={this.shipCountryDistinctData} fields= placeholder="Ship Country" popupHeight='300px' floatLabelType='Always'/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<div className="e-float-input e-control-wrapper">
<textarea id="ShipAddress" name="ShipAddress" value={data.ShipAddress} onChange={this.onChange}/>
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Ship Address</label>
</div>
</div>
</div>
</div>);
}
}
import { DataUtil } from '@syncfusion/ej2-data';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { NumericTextBoxComponent } from '@syncfusion/ej2-react-inputs';
import * as React from 'react';
import { data as orderData } from './datasource';
import { IOrderModel } from './orderModel';
export class DialogFormTemplate extends React.Component<{}, {}> {
private shipCountryDistinctData: any = DataUtil.distinct(orderData, 'ShipCountry', true );
constructor(props: object) {
super(props);
this.state = Object.assign({}, props);
}
public onChange(args: any) {
this.setState({[(args.target as HTMLInputElement).name]: args.target.value});
}
public render(): any {
this.onChange = this.onChange.bind(this);
const data: IOrderModel = this.state;
return (<div>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" type="text" disabled={!data.isAdd} value={data.OrderID} onChange={this.onChange} />
<span className="e-float-line"/>
<label className="e-float-text e-label-top"> Order ID</label>
</div>
</div>
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper" >
<input value={data.CustomerID} id="CustomerID" name="CustomerID" type="text" onChange={this.onChange} />
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Customer Name</label>
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<NumericTextBoxComponent id="Freight" format='C2' value={data.Freight} placeholder="Freight" floatLabelType='Always'/>
</div>
<div className="form-group col-md-6">
<DropDownListComponent id="ShipCountry" value={data.ShipCountry} dataSource={this.shipCountryDistinctData}
fields= placeholder="Ship Country"
popupHeight='300px' floatLabelType='Always'/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<div className="e-float-input e-control-wrapper">
<textarea id="ShipAddress" name="ShipAddress" value={data.ShipAddress} onChange={this.onChange} />
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Ship Address</label>
</div>
</div>
</div>
</div>);
}
}
The Dialog/Inline template form editors should have name attribute.
Template context
The template should be a React Component class. You can access the row information inside the Component class using props, you can bind the attribute or value based on this row information.
The following properties will be available at the time of template execution.
Property Name | Usage |
---|---|
isAdd | A Boolean property; it defines whether the current row should be a new record or not. |
In the following code example, the OrderID textbox has been disabled by using the isAdd property.
<!--The disabled attributes will be added based on the isAdd property.-->
<input id="OrderID" name="OrderID" type="text" disabled={!data.isAdd} value={data.OrderID} onChange={this.onChange} />
Get value from editor
You can read, format, and update the current editor value in the actionBegin event at the time of setting requestType to save.
In the following code example, the Freight value has been formatted and updated.
const actionBegin = (args: DialogEditEventArgs) => {
if (args.requestType === 'save' && args.form) {
/** cast string to integer value */
setValue('data.Freight',
parseFloat((args.form.querySelector("#Freight") as HTMLInputElement).value), args);
}
}
Set focus to editor
By default, the first input element in the dialog will be focused while opening the dialog.
If the first input element is in disabled or hidden state, focus the valid input element in the actionComplete event based on requestType
as beginEdit
.
const actionComplete = (args: DialogEditEventArgs) => {
// Set initail Focus
if (args.requestType === 'beginEdit') {
(args.form.elements.namedItem('CustomerID')as HTMLInputElement).focus();
}
}
Adding validation rules for custom editors
If you have used additional fields that are not present in the column model, then add the validation rules to the actionComplete event.
const actionComplete = (args: DialogEditEventArgs) => {
if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
/** Add Validation Rules */
(args.form as HTMLFormElement).ej2_instances[0].addRules('Freight', {max: 500});
}
}
Render tab component inside the dialog template
You can use tab component inside dialog edit UI using dialog template feature. The dialog template feature can be enabled by defining editSettings.mode as Dialog
and editSetting.template as a REACT Component.
The following example demonstrate the usage of tab control inside the dialog template.
import { ColumnDirective, ColumnsDirective, Edit } from '@syncfusion/ej2-react-grids';
import { GridComponent, Inject, Toolbar } from '@syncfusion/ej2-react-grids';
import * as React from "react";
import { data } from './datasource';
import { DialogFormTemplate } from './wizardTab';
function App() {
const dialogTemplate = (props) => {
const a = [props, grid];
return (<DialogFormTemplate {...a}/>);
};
const editOptions = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template: dialogTemplate };
const toolbarOptions = ['Add', 'Edit', 'Delete'];
let grid;
return <GridComponent ref={g => grid = g} dataSource={data} editSettings={editOptions} toolbar={toolbarOptions} height={265}>
<ColumnsDirective>
<ColumnDirective field='OrderID' headerText='Order ID' width='100' textAlign="Right" isPrimaryKey={true}/>
<ColumnDirective field='CustomerID' headerText='Customer ID' width='120'/>
<ColumnDirective field='ShipCountry' headerText='Ship Country' width='150'/>
</ColumnsDirective>
<Inject services={[Edit, Toolbar]}/>
</GridComponent>;
}
;
export default App;
import { ColumnDirective, ColumnsDirective, Edit } from '@syncfusion/ej2-react-grids';
import { EditSettingsModel, Grid, GridComponent, Inject, Toolbar, ToolbarItems } from '@syncfusion/ej2-react-grids';
import * as React from "react";
import { data } from './datasource';
import { IOrderModel } from './orderModel';
import { DialogFormTemplate} from './wizardTab';
function App() {
const dialogTemplate = (props: IOrderModel): any => {
const a = [props, grid]
return (<DialogFormTemplate {...a} />);
}
const editOptions: EditSettingsModel = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog', template: dialogTemplate };
const toolbarOptions: ToolbarItems[] = ['Add', 'Edit', 'Delete'];
let grid: Grid | null;
return <GridComponent ref={g => grid = g} dataSource={data} editSettings={editOptions}
toolbar={toolbarOptions} height={265}>
<ColumnsDirective>
<ColumnDirective field='OrderID' headerText='Order ID' width='100' textAlign="Right" isPrimaryKey={true} />
<ColumnDirective field='CustomerID' headerText='Customer ID' width='120' />
<ColumnDirective field='ShipCountry' headerText='Ship Country' width='150' />
</ColumnsDirective>
<Inject services={[Edit, Toolbar]} />
</GridComponent>
};
export default App;
export let data = [
{
OrderID: 10248, CustomerID: 'VINET', EmployeeID: 5, OrderDate: null,
ShipName: 'Vins et alcools Chevalier', ShipCity: 'Reims', ShipAddress: '59 rue de l Abbaye',
ShipRegion: 'CJ', ShipPostalCode: '51100', ShipCountry: 'France', Freight: 32.38, Verified: !0
},
{
OrderID: 10249, CustomerID: 'TOMSP', EmployeeID: 6, OrderDate: new Date(836505e6),
ShipName: 'Toms Spezialitäten', ShipCity: 'Münster', ShipAddress: 'Luisenstr. 48',
ShipRegion: 'CJ', ShipPostalCode: '44087', ShipCountry: 'Germany', Freight: 11.61, Verified: !1
},
{
OrderID: 10250, CustomerID: 'HANAR', EmployeeID: 4, OrderDate: new Date(8367642e5),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 65.83, Verified: !0
},
{
OrderID: 10251, CustomerID: 'VICTE', EmployeeID: 3, OrderDate: new Date(8367642e5),
ShipName: 'Victuailles en stock', ShipCity: 'Lyon', ShipAddress: '2, rue du Commerce',
ShipRegion: 'CJ', ShipPostalCode: '69004', ShipCountry: 'France', Freight: 41.34, Verified: !0
},
{
OrderID: 10252, CustomerID: 'SUPRD', EmployeeID: 4, OrderDate: new Date(8368506e5),
ShipName: 'Suprêmes délices', ShipCity: 'Charleroi', ShipAddress: 'Boulevard Tirou, 255',
ShipRegion: 'CJ', ShipPostalCode: 'B-6000', ShipCountry: 'Belgium', Freight: 51.3, Verified: !0
},
{
OrderID: 10253, CustomerID: 'HANAR', EmployeeID: 3, OrderDate: new Date(836937e6),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 58.17, Verified: !0
},
{
OrderID: 10254, CustomerID: 'CHOPS', EmployeeID: 5, OrderDate: new Date(8370234e5),
ShipName: 'Chop-suey Chinese', ShipCity: 'Bern', ShipAddress: 'Hauptstr. 31',
ShipRegion: 'CJ', ShipPostalCode: '3012', ShipCountry: 'Switzerland', Freight: 22.98, Verified: !1
},
{
OrderID: 10255, CustomerID: 'RICSU', EmployeeID: 9, OrderDate: new Date(8371098e5),
ShipName: 'Richter Supermarkt', ShipCity: 'Genève', ShipAddress: 'Starenweg 5',
ShipRegion: 'CJ', ShipPostalCode: '1204', ShipCountry: 'Switzerland', Freight: 148.33, Verified: !0
},
{
OrderID: 10256, CustomerID: 'WELLI', EmployeeID: 3, OrderDate: new Date(837369e6),
ShipName: 'Wellington Importadora', ShipCity: 'Resende', ShipAddress: 'Rua do Mercado, 12',
ShipRegion: 'SP', ShipPostalCode: '08737-363', ShipCountry: 'Brazil', Freight: 13.97, Verified: !1
},
{
OrderID: 10257, CustomerID: 'HILAA', EmployeeID: 4, OrderDate: new Date(8374554e5),
ShipName: 'HILARION-Abastos', ShipCity: 'San Cristóbal', ShipAddress: 'Carrera 22 con Ave. Carlos Soublette #8-35',
ShipRegion: 'Táchira', ShipPostalCode: '5022', ShipCountry: 'Venezuela', Freight: 81.91, Verified: !0
},
{
OrderID: 10258, CustomerID: 'ERNSH', EmployeeID: 1, OrderDate: new Date(8375418e5),
ShipName: 'Ernst Handel', ShipCity: 'Graz', ShipAddress: 'Kirchgasse 6',
ShipRegion: 'CJ', ShipPostalCode: '8010', ShipCountry: 'Austria', Freight: 140.51, Verified: !0
},
{
OrderID: 10259, CustomerID: 'CENTC', EmployeeID: 4, OrderDate: new Date(8376282e5),
ShipName: 'Centro comercial Moctezuma', ShipCity: 'México D.F.', ShipAddress: 'Sierras de Granada 9993',
ShipRegion: 'CJ', ShipPostalCode: '05022', ShipCountry: 'Mexico', Freight: 3.25, Verified: !1
},
{
OrderID: 10260, CustomerID: 'OTTIK', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Ottilies Käseladen', ShipCity: 'Köln', ShipAddress: 'Mehrheimerstr. 369',
ShipRegion: 'CJ', ShipPostalCode: '50739', ShipCountry: 'Germany', Freight: 55.09, Verified: !0
},
{
OrderID: 10261, CustomerID: 'QUEDE', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Que Delícia', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua da Panificadora, 12',
ShipRegion: 'RJ', ShipPostalCode: '02389-673', ShipCountry: 'Brazil', Freight: 3.05, Verified: !1
},
{
OrderID: 10262, CustomerID: 'RATTC', EmployeeID: 8, OrderDate: new Date(8379738e5),
ShipName: 'Rattlesnake Canyon Grocery', ShipCity: 'Albuquerque', ShipAddress: '2817 Milton Dr.',
ShipRegion: 'NM', ShipPostalCode: '87110', ShipCountry: 'USA', Freight: 48.29, Verified: !0
}
];
export let data: Object[] = [
{
OrderID: 10248, CustomerID: 'VINET', EmployeeID: 5, OrderDate: null,
ShipName: 'Vins et alcools Chevalier', ShipCity: 'Reims', ShipAddress: '59 rue de l Abbaye',
ShipRegion: 'CJ', ShipPostalCode: '51100', ShipCountry: 'France', Freight: 32.38, Verified: !0
},
{
OrderID: 10249, CustomerID: 'TOMSP', EmployeeID: 6, OrderDate: new Date(836505e6),
ShipName: 'Toms Spezialitäten', ShipCity: 'Münster', ShipAddress: 'Luisenstr. 48',
ShipRegion: 'CJ', ShipPostalCode: '44087', ShipCountry: 'Germany', Freight: 11.61, Verified: !1
},
{
OrderID: 10250, CustomerID: 'HANAR', EmployeeID: 4, OrderDate: new Date(8367642e5),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 65.83, Verified: !0
},
{
OrderID: 10251, CustomerID: 'VICTE', EmployeeID: 3, OrderDate: new Date(8367642e5),
ShipName: 'Victuailles en stock', ShipCity: 'Lyon', ShipAddress: '2, rue du Commerce',
ShipRegion: 'CJ', ShipPostalCode: '69004', ShipCountry: 'France', Freight: 41.34, Verified: !0
},
{
OrderID: 10252, CustomerID: 'SUPRD', EmployeeID: 4, OrderDate: new Date(8368506e5),
ShipName: 'Suprêmes délices', ShipCity: 'Charleroi', ShipAddress: 'Boulevard Tirou, 255',
ShipRegion: 'CJ', ShipPostalCode: 'B-6000', ShipCountry: 'Belgium', Freight: 51.3, Verified: !0
},
{
OrderID: 10253, CustomerID: 'HANAR', EmployeeID: 3, OrderDate: new Date(836937e6),
ShipName: 'Hanari Carnes', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua do Paço, 67',
ShipRegion: 'RJ', ShipPostalCode: '05454-876', ShipCountry: 'Brazil', Freight: 58.17, Verified: !0
},
{
OrderID: 10254, CustomerID: 'CHOPS', EmployeeID: 5, OrderDate: new Date(8370234e5),
ShipName: 'Chop-suey Chinese', ShipCity: 'Bern', ShipAddress: 'Hauptstr. 31',
ShipRegion: 'CJ', ShipPostalCode: '3012', ShipCountry: 'Switzerland', Freight: 22.98, Verified: !1
},
{
OrderID: 10255, CustomerID: 'RICSU', EmployeeID: 9, OrderDate: new Date(8371098e5),
ShipName: 'Richter Supermarkt', ShipCity: 'Genève', ShipAddress: 'Starenweg 5',
ShipRegion: 'CJ', ShipPostalCode: '1204', ShipCountry: 'Switzerland', Freight: 148.33, Verified: !0
},
{
OrderID: 10256, CustomerID: 'WELLI', EmployeeID: 3, OrderDate: new Date(837369e6),
ShipName: 'Wellington Importadora', ShipCity: 'Resende', ShipAddress: 'Rua do Mercado, 12',
ShipRegion: 'SP', ShipPostalCode: '08737-363', ShipCountry: 'Brazil', Freight: 13.97, Verified: !1
},
{
OrderID: 10257, CustomerID: 'HILAA', EmployeeID: 4, OrderDate: new Date(8374554e5),
ShipName: 'HILARION-Abastos', ShipCity: 'San Cristóbal', ShipAddress: 'Carrera 22 con Ave. Carlos Soublette #8-35',
ShipRegion: 'Táchira', ShipPostalCode: '5022', ShipCountry: 'Venezuela', Freight: 81.91, Verified: !0
},
{
OrderID: 10258, CustomerID: 'ERNSH', EmployeeID: 1, OrderDate: new Date(8375418e5),
ShipName: 'Ernst Handel', ShipCity: 'Graz', ShipAddress: 'Kirchgasse 6',
ShipRegion: 'CJ', ShipPostalCode: '8010', ShipCountry: 'Austria', Freight: 140.51, Verified: !0
},
{
OrderID: 10259, CustomerID: 'CENTC', EmployeeID: 4, OrderDate: new Date(8376282e5),
ShipName: 'Centro comercial Moctezuma', ShipCity: 'México D.F.', ShipAddress: 'Sierras de Granada 9993',
ShipRegion: 'CJ', ShipPostalCode: '05022', ShipCountry: 'Mexico', Freight: 3.25, Verified: !1
},
{
OrderID: 10260, CustomerID: 'OTTIK', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Ottilies Käseladen', ShipCity: 'Köln', ShipAddress: 'Mehrheimerstr. 369',
ShipRegion: 'CJ', ShipPostalCode: '50739', ShipCountry: 'Germany', Freight: 55.09, Verified: !0
},
{
OrderID: 10261, CustomerID: 'QUEDE', EmployeeID: 4, OrderDate: new Date(8377146e5),
ShipName: 'Que Delícia', ShipCity: 'Rio de Janeiro', ShipAddress: 'Rua da Panificadora, 12',
ShipRegion: 'RJ', ShipPostalCode: '02389-673', ShipCountry: 'Brazil', Freight: 3.05, Verified: !1
},
{
OrderID: 10262, CustomerID: 'RATTC', EmployeeID: 8, OrderDate: new Date(8379738e5),
ShipName: 'Rattlesnake Canyon Grocery', ShipCity: 'Albuquerque', ShipAddress: '2817 Milton Dr.',
ShipRegion: 'NM', ShipPostalCode: '87110', ShipCountry: 'USA', Freight: 48.29, Verified: !0
}];
import * as React from 'react';
export class TabOneComponent extends React.Component {
tab;
constructor(props) {
super(props);
this.state = Object.assign({}, props[0]);
this.tab = props[1];
}
onChange(args) {
this.setState({ [args.target.name]: args.target.value });
}
next() {
let valid = true;
[].slice.call(document.getElementById('tab1').querySelectorAll('[name]'))
.forEach((element) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true') {
valid = false;
}
});
if (!valid) {
return;
}
if (this.tab) {
this.tab.select(1);
}
}
render() {
this.onChange = this.onChange.bind(this);
this.next = this.next.bind(this);
const data = this.state;
return (<div id='tab1'>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" type="text" disabled={!data.isAdd} value={data.OrderID} onChange={this.onChange}/>
<span className="e-float-line"/>
<label className="e-float-text e-label-top"> Order ID</label>
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input value={data.CustomerID} id="CustomerName" name="CustomerID" type="text" onChange={this.onChange}/>
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Customer Name</label>
</div>
</div>
</div>
<div id='footer'>
<button id="nextBtn" className='e-info e-btn' type="button" style= onClick={this.next}>Next</button>
</div>
</div>);
}
}
import { TabComponent } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import { IOrderModel } from './orderModel';
export class TabOneComponent extends React.Component<{}, {}> {
public tab: TabComponent | null;
constructor(props: any) {
super(props);
this.state = Object.assign({}, props[0]);
this.tab = props[1];
}
public onChange(args: React.ChangeEvent) {
this.setState({[(args.target as HTMLInputElement).name]: (args.target as HTMLInputElement).value});
}
public next() {
let valid: boolean = true;
[].slice.call((document.getElementById('tab1') as HTMLElement).querySelectorAll('[name]'))
.forEach((element: any) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true'){
valid = false;
}
});
if (!valid) {
return
}
if (this.tab) {
this.tab.select(1);
}
}
public render(): any {
this.onChange = this.onChange.bind(this);
this.next = this.next.bind(this);
const data: IOrderModel = this.state;
return (<div id='tab1'>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper">
<input id="OrderID" name="OrderID" type="text" disabled={!data.isAdd} value={data.OrderID} onChange={this.onChange} />
<span className="e-float-line"/>
<label className="e-float-text e-label-top"> Order ID</label>
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<div className="e-float-input e-control-wrapper" >
<input value={data.CustomerID} id="CustomerName" name="CustomerID" type="text" onChange={this.onChange} />
<span className="e-float-line"/>
<label className="e-float-text e-label-top">Customer Name</label>
</div>
</div>
</div>
<div id='footer'>
<button id="nextBtn" className='e-info e-btn' type="button" style= onClick={this.next}>Next</button>
</div>
</div>);
}
}
import { DataUtil } from '@syncfusion/ej2-data';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { NumericTextBoxComponent } from '@syncfusion/ej2-react-inputs';
import * as React from 'react';
import { data as orderData } from './datasource';
export class TabTwoComponent extends React.Component {
shipCountryDistinctData = DataUtil.distinct(orderData, 'ShipCountry', true);
grid;
constructor(props) {
super(props);
this.state = Object.assign({}, props[0]);
this.grid = props[2];
}
onChange(args) {
this.setState({ [args.target.name]: args.target.value });
}
submit() {
let valid = true;
[].slice.call(document.getElementById('tab1').querySelectorAll('[name]'))
.forEach((element) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true') {
valid = false;
}
});
if (!valid) {
return;
}
if (this.grid) {
this.grid.endEdit();
}
}
render() {
this.submit = this.submit.bind(this);
const data = this.state;
return (<div id='tab2'>
<div className="form-row">
<div className="form-group col-md-6">
<NumericTextBoxComponent id="Freight" format='C2' value={data.Freight} placeholder="Freight" floatLabelType='Always'/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<DropDownListComponent id="ShipCountry" value={data.ShipCountry} dataSource={this.shipCountryDistinctData} fields= placeholder="Ship Country" popupHeight='300px' floatLabelType='Always'/>
</div>
</div>
<div id='footer'>
<button id="submitBtn" className="e-info e-btn" type="button" style= onClick={this.submit}>SUBMIT</button>
</div>
</div>);
}
}
import { DataUtil } from '@syncfusion/ej2-data';
import { Grid } from '@syncfusion/ej2-grids';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { NumericTextBoxComponent } from '@syncfusion/ej2-react-inputs';
import * as React from 'react';
import { data as orderData } from './datasource';
import { IOrderModel } from './orderModel';
export class TabTwoComponent extends React.Component<{}, {}> {
private shipCountryDistinctData: any = DataUtil.distinct(orderData, 'ShipCountry', true );
private grid: Grid | null;
constructor(props: any) {
super(props);
this.state = Object.assign({}, props[0]);
this.grid = props[2];
}
public onChange(args: React.ChangeEvent) {
this.setState({[(args.target as HTMLInputElement).name]: (args.target as HTMLInputElement).value});
}
public submit() {
let valid: boolean = true;
[].slice.call((document.getElementById('tab1') as HTMLElement).querySelectorAll('[name]'))
.forEach((element: any) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true'){
valid = false;
}
});
if (!valid) {
return
}
if (this.grid) {
this.grid.endEdit();
}
}
public render(): any {
this.submit = this.submit.bind(this);
const data: IOrderModel = this.state;
return (<div id='tab2'>
<div className="form-row">
<div className="form-group col-md-6">
<NumericTextBoxComponent id="Freight" format='C2' value={data.Freight} placeholder="Freight" floatLabelType='Always'/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<DropDownListComponent id="ShipCountry" value={data.ShipCountry} dataSource={this.shipCountryDistinctData}
fields= placeholder="Ship Country"
popupHeight='300px' floatLabelType='Always'/>
</div>
</div>
<div id='footer'>
<button id="submitBtn" className="e-info e-btn" type="button" style= onClick={this.submit}>SUBMIT</button>
</div>
</div>);
}
}
import { TabComponent, TabItemDirective, TabItemsDirective } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import { TabOneComponent } from './tabOne';
import { TabTwoComponent } from './tabTwo';
export class DialogFormTemplate extends React.Component {
grid;
tab;
constructor(props) {
super(props);
this.state = Object.assign({}, props[0]);
this.grid = props[1];
}
tabOne() {
const tab = [this.state, this.tab, this.grid];
return (<TabOneComponent {...tab}/>);
}
tabTwo() {
const tab = [this.state, this.tab, this.grid];
return (<TabTwoComponent {...tab}/>);
}
next() {
let valid = true;
[].slice.call(document.getElementById('tab1')
.querySelectorAll('[name]')).forEach((element) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true') {
valid = false;
}
});
if (!valid) {
return false;
}
return true;
}
selecting(e) {
{
if (e.isSwiped) {
e.cancel = true;
}
if (e.selectingIndex === 1) {
e.cancel = !this.next();
}
}
}
render() {
this.tabOne = this.tabOne.bind(this);
this.tabTwo = this.tabTwo.bind(this);
this.selecting = this.selecting.bind(this);
return (<div>
<TabComponent id='defaultTab' ref={t => this.tab = t} selecting={this.selecting}>
<TabItemsDirective>
<TabItemDirective header=text content={this.tabOne}/>
<TabItemDirective header=text content={this.tabTwo}/>
</TabItemsDirective>
</TabComponent>
</div>);
}
}
import { Grid } from '@syncfusion/ej2-grids';
import { SelectingEventArgs } from '@syncfusion/ej2-navigations';
import { TabComponent, TabItemDirective, TabItemsDirective } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import { TabOneComponent } from './tabOne';
import { TabTwoComponent } from './tabTwo';
export class DialogFormTemplate extends React.Component<{}, {}> {
private grid: Grid;
private tab: TabComponent | null;
constructor(props: any) {
super(props);
this.state = Object.assign({}, props[0]);
this.grid = props[1];
}
public tabOne() {
const tab = [this.state, this.tab, this.grid];
return (<TabOneComponent {...tab} />);
}
public tabTwo() {
const tab = [this.state, this.tab, this.grid];
return (<TabTwoComponent {...tab} />);
}
public next() {
let valid: boolean = true;
[].slice.call((document.getElementById('tab1') as HTMLElement)
.querySelectorAll('[name]')).forEach((element: any) => {
element.form.ej2_instances[0].validate(element.name);
if (element.getAttribute('aria-invalid') === 'true'){
valid = false;
}
});
if (!valid) {
return false;
}
return true;
}
public selecting(e: SelectingEventArgs) {
{
if(e.isSwiped) {e.cancel = true;}
if(e.selectingIndex === 1) {e.cancel = !this.next();}
}
}
public render(): any {
this.tabOne = this.tabOne.bind(this);
this.tabTwo = this.tabTwo.bind(this);
this.selecting = this.selecting.bind(this);
return (<div>
<TabComponent id='defaultTab' ref={t => this.tab = t} selecting={this.selecting}>
<TabItemsDirective>
<TabItemDirective header= { { 'text': 'Details' } } content={this.tabOne} />
<TabItemDirective header= { { 'text': 'Verify' } } content={this.tabTwo}/>
</TabItemsDirective>
</TabComponent>
</div>);
}
}