HelpBot Assistant

How can I help you?

Edit in Angular Grid Component

19 Mar 202624 minutes to read

The Syncfusion® Angular Grid component includes built-in editing features for creating, reading, updating, and deleting data directly in the grid. This eliminates the need for separate forms and allows data modification within a single interface. The grid editing provides powerful options through multiple edit modes, customizable triggers, and flexible data handling.

Set up editing

Before using editing in the grid, understand that the component needs the EditService module to unlock all editing features. The EditService is a service that powers all create, read, update, and delete operations. Without it, editing features cannot work.

Inject the EditService to the providers array.

import { Component, OnInit } from '@angular/core';
import { GridComponent, EditService, ToolbarService, PageService, SortService } from '@syncfusion/ej2-angular-grids';

@Component({
    selector: 'app-root',
    providers: [EditService, ToolbarService, PageService, SortService]
    template: `
    <ejs-grid [dataSource]='data'>
    </ejs-grid>`, 
})
export class AppComponent {
  public data: any[] = [...];
}

Enable editing

To enable editing functionality directly within the grid, configure the allowEditing, allowAdding, and allowDeleting properties within the editSettings to true.

Property Purpose
allowEditing Enable editing of existing records
allowAdding Enable adding new records
allowDeleting Enable deleting records

Editing requires a primary key column to support full CRUD functionality. Define the primary key by setting columns.isPrimaryKey to true on the relevant column.

Edit actions can be initiated by double-clicking a row or by selecting a row and clicking the Edit button in the toolbar. Records can be added by clicking the Add button in the toolbar or via an external trigger that invokes the addRecord method. Use Save and Cancel to commit or discard changes from the toolbar during edit mode. Deletion is performed by selecting the target row and clicking the Delete button.

To explore available edit modes and types in Angular Grid, refer to the following video:

import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, EditSettingsModel, GridModule, PageService, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
    imports: [ 
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule
        ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<ejs-grid [dataSource]='data' [editSettings]='editSettings' height='273px'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' [validationRules]='orderIDRules'
                    isPrimaryKey='true' width=100></e-column>
                    <e-column field='CustomerID' headerText='Customer ID' [validationRules]='customerIDRules' width=120></e-column>
                    <e-column field='Freight' headerText='Freight' textAlign= 'Right' editType= 'numericedit'
                     width=120  [validationRules]='freightRules' format= 'C2'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' editType= 'dropdownedit' width=150></e-column>
                </e-columns>
                </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbar?: ToolbarItems[];
    public orderIDRules?: Object;
    public customerIDRules?: Object;
    public freightRules?: Object;

    ngOnInit(): void {
        this.data = data;
        this.orderIDRules = { required: true, number: true };
        this.customerIDRules = { required: true };
        this.freightRules = { required: true,min:1, max:1000 };
        this.editSettings = {  allowEditing: true, allowAdding: true, allowDeleting: true, };
    }
  }
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

  • If columns.isIdentity is enabled, the column will be treated as read-only when editing or adding records.
  • Use columns.allowEditing set to false to disable editing for specific columns.
  • The Insert key adds a new row, and the Delete key deletes the selected row in the grid.

Toolbar with edit options

The toolbar with edit option feature provides a built-in toolbar that includes items for editing actions. Using the toolbar, users can easily modify, update, or cancel grid data edits.

Configure the toolbar property of the Grid component to enable this feature. The toolbar property defines the items displayed in the grid toolbar. Include relevant items like Edit, Add, Delete, Update, and Cancel within the toolbar property to enable edit options in the toolbar.

The following example demonstrates to enable the toolbar with edit option in the grid:

import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, EditSettingsModel, GridModule, PageService, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
    imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule
        ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<ejs-grid [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar' height='273px'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' [validationRules]='orderIDRules' textAlign='Right' isPrimaryKey='true' width=100></e-column>
                    <e-column field='CustomerID' headerText='Customer ID' [validationRules]='customerIDRules' width=120></e-column>
                    <e-column field='Freight' headerText='Freight' [validationRules]='freightRules' textAlign= 'Right' width=120 format= 'C2'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' [validationRules]='shipCountryRules'  width=150></e-column>
                </e-columns>
                </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbar?: ToolbarItems[];
    public orderIDRules?: Object;
    public customerIDRules?: Object;
    public freightRules?: Object;
    public shipCountryRules?: Object;

    ngOnInit(): void {
        this.data = data;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true };
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true, number: true };
        this.customerIDRules = { required: true };
        this.freightRules =  {required: true ,number: true };
        this.shipCountryRules = { required: true };
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Disable editing for specific columns

The Grid component provides the option to disable editing for specific columns. This is useful when certain columns should remain read-only, such as columns containing calculated values, IDs, or system-generated data.

Static column disabling

To permanently disable editing for a column, set the allowEditing property to false on the column. This prevents editing for that column across all rows:

<e-column field='OrderID' [allowEditing]='false'></e-column>

Dynamic column disabling

To disable editing for a column based on application interaction or conditions, use the allowEditing property of the columns object. Set this property to false to prevent editing for that specific column.

The following example demonstrates to disable editing for selected columns dynamically in the Grid.

import { data } from './datasource';
import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, DropDownListComponent, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { Column, EditService, GridComponent, GridModule, PageService, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { ChangeEventArgs } from '@syncfusion/ej2-dropdowns';

@Component({
  imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule,
        DropDownListModule
      ],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  public data?: Object[];
  @ViewChild('grid') public grid?: GridComponent;
  @ViewChild('dropdown') public dropdown?: DropDownListComponent;
  public editSettings?: Object;
  public toolbar?: string[];
  public orderIDRules?: Object;
  public customerIDRules?: Object;
  public freightRules?: Object;
  public editparams?: Object;
  public pageSettings?: Object;
  public alignmentData: Object[] = [
    { text: 'Order ID', value: 'OrderID' },
    { text: 'Customer ID', value: 'CustomerID' },
    { text: 'Freight', value: 'Freight' },
    { text: 'Order Date', value: 'OrderDate' },
    { text: 'Ship Country', value: 'ShipCountry' },
  ];
  public dropdownFields: Object = { text: 'text', value: 'value' }; // Define fields for the dropdown
  public currentColumn?: Column;
  public ngOnInit(): void {
    this.data = data;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
    };
    this.toolbar = ['Add', 'Delete', 'Update', 'Cancel'];
    this.orderIDRules = { required: true, number: true };
    this.customerIDRules = { required: true };
    this.freightRules = { required: true };
    this.editparams = { params: { popupHeight: '300px' } };
    this.pageSettings = { pageCount: 5 };
  }

  public changeAlignment(args: ChangeEventArgs): void {
    // Reset the allowEditing property of the previously selected column
    if (this.currentColumn) {
      this.currentColumn.allowEditing = true;
    }
    // Update the 'allowEditing' property for the selected column
    this.currentColumn = this.grid?.getColumnByField((args.value as string)) as Column;
    this.currentColumn.allowEditing = false;
  }
}
<div style="display: flex">
    <label style="padding: 30px 17px 0 0;"> Select column to disable editing:</label>
    <ejs-dropdownlist #dropdown style="padding: 26px 0 0 0" index="0" width="150" 
      [dataSource]="alignmentData" [fields]="dropdownFields" (change)="changeAlignment($event)">
    </ejs-dropdownlist>
  </div>
<div>
    <ejs-grid #grid id='Batchgrid' [dataSource]='data' allowPaging='true' [pageSettings]='pageSettings'
      [editSettings]='editSettings' [toolbar]='toolbar' height='180px'>
      <e-columns>
        <e-column field='OrderID' headerText='Order ID' width='120' textAlign='Right' isPrimaryKey='true'
          [validationRules]='orderIDRules'></e-column>
        <e-column field='CustomerID' headerText='Customer ID' width='120' [validationRules]='customerIDRules'
          ></e-column>
        <e-column field='Freight' headerText='Freight' width='120' format='C2' textAlign='Right'
          editType='numericedit' [validationRules]='freightRules' >
        </e-column>
        <e-column field='OrderDate' headerText='Order Date' width='130' format='yMd' editType='datepickeredit'
          textAlign='Right' ></e-column>
        <e-column field='ShipCountry' headerText='Ship Country' width='150' editType='dropdownedit'
          [edit]='editparams' ></e-column>
      </e-columns>
    </ejs-grid>
</div>
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

  • If isPrimaryKey is enabled, editing is automatically disabled for that column.
  • To disable editing for a specific row using the actionBegin event. See example here.
  • To disable editing for a particular cell using the cellEdit event. See example here.

Editing template columns

Customizing the editing experience for specific columns is possible by defining an editing template. Use the field property to connect the column with its corresponding data field.

In this example, the “Ship Country” column is rendered with a template:

import { FormsModule } from '@angular/forms';
import { GridModule, EditService, ToolbarService, SortService, PageService } from '@syncfusion/ej2-angular-grids';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { DropDownListModule, MultiSelectModule, AutoCompleteModule };
import { Component, OnInit } from '@angular/core';
import { data } from './datasource';
import { EditSettingsModel, ToolbarItems } from '@syncfusion/ej2-angular-grids';

@Component({
imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule
    ],

providers: [EditService, ToolbarService, SortService, PageService],
standalone: true,
    selector: 'app-root',
    template: `<ejs-grid [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar' height='273px'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' [validationRules]='orderIDRules' textAlign='Right' isPrimaryKey='true' width=100></e-column>
                    <e-column field='CustomerID' [validationRules]='customerIDRules'  headerText='Customer ID' width=120></e-column>
                    <e-column field='Freight' headerText='Freight' textAlign= 'Right'
                    editType= 'numericedit' width=120  [validationRules]='freightRules' format= 'C2'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' editType= 'dropdownedit' width=150>
                        <ng-template #template let-data>
                            <a href="#">{{data.ShipCountry}}</a>
                        </ng-template>
                    </e-column>
                </e-columns>
                </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbar?: ToolbarItems[];
    public orderIDRules?: object;
    public customerIDRules?: object;
    public freightRules?: Object;

    ngOnInit(): void {
        this.data = data;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true };
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true };
        this.freightRules =  { min:1, max:1000 };
        this.customerIDRules = { required: true };

    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Customizing the delete confirmation dialog

By default, the Grid shows a confirmation dialog when attempting to delete a row. The appearance and content of this dialog can be customized to match application requirements. Customization can include changing the dialog header, icons, or button text.

To customize the delete confirmation dialog, utilize the toolbarClick event. This event is triggered when a toolbar action is performed and allows modification of dialog properties.

  • Enable the confirmation dialog for deletions by setting showDeleteConfirmDialog to true in editSettings.
  • Refer to the grid Default text documentation for localization options.

The following example demonstrates to customize the delete confirmation dialog using the toolbarClick event.

import { data } from './datasource';
import { Component, ViewChild } from '@angular/core';
import { EditService, GridComponent, GridModule, PageService, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { ClickEventArgs, Item } from '@syncfusion/ej2-angular-navigations';
import { L10n } from '@syncfusion/ej2-base';

L10n.load({
  'en-US': {
      grid: {
          'OKButton':'YES',
          'CancelButton':'Discard' ,
          'ConfirmDelete': 'Are you sure you want to delete the selected Record?' 
      }
  }
});
@Component({
  imports: [ GridModule],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `
    <ejs-grid #grid [dataSource]="data" [editSettings]="editSettings" [toolbar]="toolbar" 
        (toolbarClick)="toolbarClick($event)" height='250px'>
      <e-columns>
        <e-column field="OrderID" headerText="Order ID" isPrimaryKey="true" width="120"></e-column>
        <e-column field="CustomerID" headerText="Customer ID" width="150"></e-column>
        <e-column field="ShipCountry" headerText="Ship Country" width="120"></e-column>
        <e-column field="ShipCity" headerText="Ship City" width="130"></e-column>
      </e-columns>
    </ejs-grid>
  `,
})
export class AppComponent {

  @ViewChild('grid')
  public grid?: GridComponent;
  public data?: Object[];
  public editSettings?: Object;
  public toolbar?: Object;

  public ngOnInit(): void {
    this.data = data;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
      showDeleteConfirmDialog: true,
    };

    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
  }
  toolbarClick(args: ClickEventArgs): void {
    if ((args.item as Item).text === 'Delete') {
        const dialogObj= ((this.grid as GridComponent).editModule as any).dialogObj   ;
        dialogObj.header = 'Delete Confirmation Dialog';
        dialogObj.showCloseIcon = true;    
    }
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Update boolean column value with a single click

The grid allows boolean column values to be toggled with a single click in normal editing mode. Use the column template feature to render a CheckBox for direct interaction.

The following example demonstrates to render a CheckBox component as a template in the “Verified” column to enable single-click editing:

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { CheckBoxModule } from '@syncfusion/ej2-angular-buttons';
import { EditService, EditSettingsModel, GridComponent, GridModule, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';

@Component({
    imports: [ GridModule,CheckBoxModule ],
    providers: [EditService, ToolbarService ],
    standalone: true,
    selector: 'app-root',
    template: `
        <ejs-grid #grid [dataSource]="data" [editSettings]="editSettings" [toolbar]="toolbar" height="250px">
          <e-columns>
            <e-column field="OrderID" headerText="Order ID" isPrimaryKey="true" textAlign="Right" width="120" [validationRules]='orderIDRules'></e-column>
            <e-column field="CustomerID" headerText="Customer Name" width="120" [validationRules]='customerIDRules'></e-column>
            <e-column field="OrderDate" headerText="Order Date" editType="datepickeredit" format="M/d/yy" textAlign="Right" [validationRules]='dateRules' width="130" type="date"></e-column>
            <e-column field="Freight" headerText="Freight" format="C2" textAlign="Right" width="90" [validationRules]='freightRules'></e-column>
            <e-column field="Verified" headerText="Verified" textAlign="Right" width="90" [validationRules]='verifiedRules'>
              <ng-template #template let-data>
                <ejs-checkbox [(checked)]="data.Verified" (change)="onVerifiedChange(data)"></ejs-checkbox>
              </ng-template>
            </e-column>
          </e-columns>
        </ejs-grid>`
})
export class AppComponent implements OnInit {

   @ViewChild('grid')
    public grid: GridComponent | undefined;
    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbar?: ToolbarItems[];
    public orderData?: object|any;
    public orderIDRules?: object;
    public customerIDRules?: object;
    public freightRules?: object;
    public dateRules?: object;
    public verifiedRules?: object;

    ngOnInit(): void {
        this.data = data;
        this.orderIDRules = { required: true };
        this.customerIDRules = { required: true  };
        this.dateRules= { required: true  };
        this.verifiedRules= { required: true  };
        this.freightRules = { required: true, min: 1, max: 1000 };
        this.editSettings = {
            allowEditing: true,
            allowAdding: true,
            allowDeleting: true,
        };
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    }

  public onVerifiedChange(rowData: object | any): void {
    const rowIndex = (this.grid as GridComponent).getRowIndexByPrimaryKey(
      rowData.OrderID
    );
    (this.grid as GridComponent).updateRow(rowIndex, rowData);
  }    
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Editing enum column values

Enum columns contain predefined list values (enumerated data). Instead of allowing free-form text input, using a dropdown editor ensures data consistency and prevents invalid entries. The editTemplate property enables custom editors for enum data.

The following example demonstrates to render a DropDownList component as an edit template for the “Employee Feedback” column, binding it to a predefined list of enum values:

import { data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, ComboBoxModule, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, EditSettingsModel, ForeignKeyService, GridModule, PageService, SaveEventArgs, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
  imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule,
        DropDownListModule,
        ComboBoxModule
      ],
  providers: [EditService, ToolbarService, SortService, ForeignKeyService,PageService],
  standalone: true,
  selector: 'app-root',
  template: `<ejs-grid [dataSource]="details" [editSettings]="editSettings" [toolbar]="toolbar" (actionBegin)="actionBegin($event)" height='250px'>
    <e-columns>
      <e-column field="OrderID" headerText="Order ID" isPrimaryKey="true" textAlign="Right" width="90"></e-column>
      <e-column field="CustomerID" headerText="Employee Name" textAlign="Left" width="100"></e-column>
      <e-column field="FeedbackDetails" headerText="Employee Feedback" textAlign="Left" width="120">
        <ng-template #editTemplate let-data>
          <ejs-dropdownlist [(ngModel)]="orderData.FeedbackDetails" [dataSource]="dropDownEnumValue" [fields]="dropdownFields" popupHeight="150px">
          </ejs-dropdownlist>
        </ng-template>
      </e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

  public details?: EmployeeDetails[];
  public editSettings?: EditSettingsModel;
  public toolbar?: ToolbarItems[];
  public orderData?: EmployeeDetails | any;
  public orderIDRules?: object;
  public customerIDRules?: object;
  public freightRules?: object;
  public dropDownEnumValue: string[] = [];
  public dropdownFields: Object = { text: 'FeedbackDetails', value: 'FeedbackDetails' };

  ngOnInit(): void {
    this.details = data as EmployeeDetails[];
    this.orderIDRules = { required: true };
    this.freightRules = { required: true, min: 1, max: 1000 };
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
    };
    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    this.dropDownEnumValue = Object.keys(Feedback).filter((key: string) => !isNaN(Number((Feedback as any)[key])));
  }

  actionBegin(args: SaveEventArgs) {
    if (args.requestType === 'beginEdit' || args.requestType === 'add') {
      this.orderData = Object.assign({}, args.rowData);
    }
    if (args.requestType === 'save') {
      (args.data as EmployeeDetails)['FeedbackDetails'] = this.orderData['FeedbackDetails'];
    }
  }

}

export interface EmployeeDetails {
  OrderID: number;
  CustomerID: string;
  FeedbackDetails: Feedback;
}

export enum Feedback {
  Positive = 0,
  Negative = 1,
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Edit complex columns

Complex columns contain nested data objects (such as “Name.FirstName”). When editing complex data with custom input elements, the binding syntax differs from simple columns. Use the underscore operator (___) instead of the dot operator (.) to correctly bind nested properties in edit templates.

The following example demonstrates to edit complex nested data. The “FirstName” and “LastName” properties (nested under “Name”) are edited using input elements with names defined as “Name__FirstName” and “Name__LastName”:

import { complexData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, EditSettingsModel, GridModule, PageService, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
  imports: [  
          GridModule,
          DatePickerAllModule,
          FormsModule,
          TimePickerModule,
          FormsModule,
          TextBoxModule,
          MultiSelectModule,
          AutoCompleteModule,
          DropDownListModule
      ],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `
    <ejs-grid #grid [dataSource]="data" height="295px" [editSettings]="editSettings" [toolbar]="toolbar" >
      <e-columns>
        <e-column field="EmployeeID" headerText="Employee ID" textAlign="Right" width="120"></e-column>
        <e-column field="Name.FirstName" headerText="First Name" width="200">
          <ng-template #editTemplate let-data>
              <input class="e-input" name="Name___FirstName" type="text" id="Name___FirstName" [value]="data.Name.FirstName" />
          </ng-template>
        </e-column>
        <e-column field="Name.LastName" headerText="Last Name" width="200">
          <ng-template #editTemplate let-data>
              <input class="e-input" type="text" name="Name___LastName" id="Name___LastName" [value]="data.Name.LastName" >
          </ng-template>
        </e-column>
        <e-column field="Title" headerText="Title" width="150" ></e-column>
      </e-columns>
    </ejs-grid>
  `,
})
export class AppComponent implements OnInit {
    
  public data?: object[];
  public editSettings?: EditSettingsModel;
  public toolbar?: ToolbarItems[];

  ngOnInit(): void {
    this.data = complexData;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
    };
    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Editing foreign key columns

The Syncfusion Grid offers a powerful editing feature for foreign key columns, enhancing the default rendering of the DropDownList component during editing. This flexibility is particularly useful for customizing the editor for foreign key columns. By default, the Syncfusion Grid renders the DropDownList component as the editor for foreign key columns during editing. However, this behavior can be enhanced and customized by leveraging the editTemplate property for the column using ng-template. The editTemplate property allows specification of a cell edit template that serves as an editor for a particular column, accepting either a template string or an HTML element ID.

In the following code example, the “Employee Name” is a foreign key column. When editing, the ComboBox component is rendered instead of DropDownList.

import { columnDataType, data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, ComboBoxModule, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, ForeignKeyService, GridModule, PageService, SortService, ToolbarService, EditSettingsModel,ToolbarItems,SaveEventArgs } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
  imports: [
      GridModule,
      DatePickerAllModule,
      FormsModule,
      TimePickerModule,
      FormsModule,
      TextBoxModule,
      MultiSelectModule,
      AutoCompleteModule,
      DropDownListModule,
      ComboBoxModule
      ],
  providers: [EditService, ToolbarService, SortService, ForeignKeyService,PageService],
  standalone: true,
  selector: 'app-root',
  template: `
        <ejs-grid [dataSource]="data" height="220" [editSettings]="editSettings"  [allowPaging]="true" [toolbar]="toolbar" (actionBegin)=" actionBegin($event)">
          <e-columns>
              <e-column field="OrderID" headerText="Order ID" textAlign="Right" isPrimaryKey="true" width="120"></e-column>
              <e-column field="EmployeeID" headerText="Employee Name" foreignKeyValue='FirstName'  [dataSource]="employeeData" width="150">
                  <ng-template #editTemplate let-data>
                      <ejs-combobox [(value)]="orderData.EmployeeID" [dataSource]="employees" [fields]="comboFields" >
                      </ejs-combobox>
                  </ng-template>
              </e-column>
              <e-column field="OrderDate" headerText="Order Date" format="yMd" type="date" textAlign="Right" width="130"></e-column>
              <e-column field="Freight" headerText="Freight" format="C2" textAlign="Right" width="120"></e-column>
          </e-columns>
      </ejs-grid>`
})
export class AppComponent implements OnInit {
  public data?: object[];
  public editSettings?: EditSettingsModel;
  public toolbar?: ToolbarItems[];
  public orderData?: object | any;
  public orderIDRules?: object;
  public customerIDRules?: object;
  public employeeIDRules?: object;
  public employees: object[] = [];
  public employeeData: Object[] = employeeData;
  public comboFields: Object = { text: 'FirstName', value: 'EmployeeID' };

  ngOnInit(): void {
    this.data = data;
    this.orderIDRules = { required: true };
    this.customerIDRules = { required: true };
    this.employeeIDRules = { required: true };
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
    };
    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    this.employees = [
      { FirstName: 'Nancy', EmployeeID: 1 },
      { FirstName: 'Andrew', EmployeeID: 6 },
      { FirstName: 'Janet', EmployeeID: 3 },
      { FirstName: 'Margaret', EmployeeID: 4 },
      { FirstName: 'Steven', EmployeeID: 5 },
      { FirstName: 'Laura', EmployeeID: 8 }
    ];
  }
  actionBegin(args: SaveEventArgs) {
    if (args.requestType === 'beginEdit' || args.requestType === 'add') {
      this.orderData = Object.assign({}, args.rowData);
    }
    if (args.requestType === 'save') {
      (args.data as columnDataType)['EmployeeID'] = this.orderData['EmployeeID'];
    }
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Preventing duplicate rows with custom validation

The Syncfusion Angular Grid enables enforcement of constraints to prevent duplicate rows by customizing the validation logic within the grid setup. This ensures data integrity by restricting duplicate entries in the “Order ID” column.

To prevent adding duplicate rows in the grid, follow these steps:

  1. Implement Custom Validation: Define the “orderIdCustomValidation” function to check whether the entered “Order ID” already exists in the dataSource. This allows editing an existing row without triggering a duplicate error.

  2. Add Dynamic Validation Rules: Create the “orderIDRules” object to enforce unique “Order ID” values. Dynamically add this rule to the form during the save action.

  3. Handle Validation in the actionBegin event: In the actionBegin event, check if the requestType is save. Apply the validation rule before saving and cancel the action args.cancel = true if the validation fails.

For server-side validation to prevent adding duplicate rows, refer to the detailed guidance provided in our knowledge base. To display the Grid’s validation tooltip instead of the alert used in the knowledge base, call the grid.editModule.formObj.validate() method in the Ajax/Fetch success function to show the Grid’s tooltip validation for the server side.

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { EditService, EditSettingsModel, GridComponent, GridModule, PageService, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';

@Component({
  imports: [GridModule, FormsModule],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `<ejs-grid #grid [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar' height='273'  (actionBegin)="actionBegin($event)">
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' isPrimaryKey='true' width=100></e-column>
                    <e-column field='CustomerID' headerText='Customer ID' [validationRules]='customerIDRules' width=120></e-column>
                    <e-column field='Freight' headerText='Freight' textAlign= 'Right' editType= 'numericedit' width=120 format= 'C2'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' editType= 'dropdownedit' width=150></e-column>
                </e-columns>
             </ejs-grid>`
})
export class AppComponent implements OnInit {
  @ViewChild('grid') public grid?: GridComponent;
  public data?: object[];
  public editSettings?: EditSettingsModel;
  public toolbar?: ToolbarItems[];
  public orderIDRules?: object;
  public customerIDRules?: object;

  public orderIdCustomValidation(args: any): boolean {
    return (this.grid?.dataSource as Object[]).every((data: any) => {
      return data['OrderID'] + '' !== args['value'] || data['OrderID'] === (this.grid as any).editModule.editModule.args.rowData['OrderID']
    });
  }

  public actionBegin(args: any):void {
    if (args.requestType === 'save') {
      (this.grid as GridComponent).editModule.formObj.addRules('OrderID', this.orderIDRules as object);  // Add form validation rules dynamically.
      if (!(this.grid as GridComponent).editModule.formObj.validate()) { // Check dynamically added validation rules.
        args.cancel = true; // Prevent adding duplicate data action.
      }
      (this.grid as GridComponent).editModule.formObj.removeRules('OrderID'); // Remove form validation rules dynamically.
    }
  }
  
  public ngOnInit(): void {
    this.data = data;
    this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' };
    this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    this.orderIDRules = { required: true, min: [this.orderIdCustomValidation.bind(this), 'The entered value already exists in the column OrderID. Please enter a unique value.'] };
    this.customerIDRules = { required: true };
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Performing CRUD actions externally

By default, the grid provides built-in editing through toolbars and inline editing. However, CRUD operations can also be triggered programmatically from external controls (custom buttons, forms, or panels outside the grid). This allows full control over when and how data operations occur.

Using a separate toolbar

To perform CRUD operations externally, use the following methods:

Method Purpose
addRecord Add a new record (shows edit form if no data provided)
startEdit Begin editing the selected row
deleteRecord Delete the selected row
endEdit Save changes when grid is in edit state
closeEdit Cancel editing without saving

The following example demonstrates external CRUD operations with a custom toolbar.

import { data } from './datasource';
import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, DropDownListModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, GridComponent, GridModule, PageService, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { ToolbarModule } from '@syncfusion/ej2-angular-navigations';
import { ClickEventArgs } from '@syncfusion/ej2-buttons';

@Component({
  imports: [
      GridModule,
      DatePickerAllModule,
      FormsModule,
      TimePickerModule,
      FormsModule,
      TextBoxModule,
      MultiSelectModule,
      AutoCompleteModule,
      ToolbarModule ,
      DropDownListModule
      ],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `
      <div style="display: flex">
        <ejs-toolbar (clicked)="onToolbarClick($event)">
          <e-items>
              <e-item text="Add" id="add"></e-item>
              <e-item text="Edit" id="edit"></e-item>
              <e-item text="Delete" id="delete"></e-item>
              <e-item text="Update" id="update"></e-item>
              <e-item text="Cancel" id="cancel"></e-item>
          </e-items>
      </ejs-toolbar>
      </div>
      <div >
          <ejs-grid #grid id='Batchgrid' [dataSource]='data' allowPaging='true' [pageSettings]='pageSettings'
            [editSettings]='editSettings' height='220px'>
            <e-columns>
              <e-column field='OrderID' headerText='Order ID' width='120' textAlign='Right' isPrimaryKey='true'
                [validationRules]='orderIDRules'></e-column>
              <e-column field='CustomerID' headerText='Customer ID' width='120' [validationRules]='customerIDRules'
                ></e-column>
              <e-column field='Freight' headerText='Freight' width='120' format='C2' textAlign='Right'
                editType='numericedit' [validationRules]='freightRules' >
              </e-column>
              <e-column field='OrderDate' headerText='Order Date' width='130' format='yMd' editType='datepickeredit'
                textAlign='Right' ></e-column>
              <e-column field='ShipCountry' headerText='Ship Country' width='150' editType='dropdownedit'
                [edit]='editparams' ></e-column>
            </e-columns>
          </ejs-grid>
      </div>`,
  })
export class AppComponent {
  public data?: Object[];
  @ViewChild('grid') public grid?: GridComponent;
  public editSettings?: Object;
  public orderIDRules?: Object;
  public customerIDRules?: Object;
  public freightRules?: Object;
  public editparams?: Object;
  public pageSettings?: Object;
   public currentColumn: any;
  public ngOnInit(): void {
    this.data = data;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
    };
    this.orderIDRules = { required: true, number: true };
    this.customerIDRules = { required: true };
    this.freightRules = { required: true };
    this.editparams = { params: { popupHeight: '300px' } };
    this.pageSettings = { pageCount: 5 };
  }

  public onToolbarClick(args: ClickEventArgs): void {
    switch ((args as any).item.id) {
      case 'add':
        (this.grid as GridComponent).addRecord();
        break;
      case 'edit':
        (this.grid as GridComponent).startEdit();
        break;
      case 'delete':
        (this.grid as GridComponent).deleteRecord();
        break;
      case 'update':
        (this.grid as GridComponent).endEdit();
        break;
      case 'cancel':
        (this.grid as GridComponent).closeEdit();
        break;
    }
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Using an external form

Instead of editing data in the grid itself, custom forms can be used to edit selected rows. When a row is selected in the grid, the corresponding data is populated in an external form. Changes made in the external form update the grid data.

The rowSelected event can be used to capture row selection and populate external form fields with the selected row’s data. The following example demonstrates editing using an external form.

import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, GridComponent, GridModule, PageService, RowSelectEventArgs, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { NumericTextBoxAllModule, TextBoxModule } from '@syncfusion/ej2-angular-inputs';
import{ data } from "./datasource"

@Component({
  imports: [ GridModule, FormsModule, TextBoxModule, ButtonModule, NumericTextBoxAllModule,DropDownListModule],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `
         <div class="row" >
              <div class="col-xs-6 col-md-3">
                <div>
                  <div class="form-row">
                    <div class="form-group col-md-12">
                      <label for="orderedit">OrderID</label>
                      <input class="form-control" [(ngModel)]="selectedProduct.OrderID" type="number" disabled />
                    </div>
                  </div>
                  <div class="form-row">
                    <div class="form-group col-md-12">
                      <label for="customeredit">CustomerID</label>
                      <ejs-textbox [(value)]="selectedProduct.CustomerID"></ejs-textbox>
                    </div>
                  </div>
                  <div class="form-row">
                    <div class="form-group col-md-12">
                      <label for="freightedit">Frieght</label>
                      <ejs-numerictextbox [(value)]="selectedProduct.Freight"></ejs-numerictextbox>
                    </div>
                  </div>
                  <div class="form-row">
                    <div class="form-group col-md-12">
                      <label for="countryedit">ShipCountry</label>
                      <ejs-dropdownlist [(value)]="selectedProduct.ShipCountry" [dataSource]="dropdown" [fields]="dropdownFields">
                      </ejs-dropdownlist>
                    </div>
                  </div>
                </div>
                <button ejs-button id="btn" (click)="save()">Save</button>
              </div>
              <div class="col-xs-6 col-md-9">
                <ejs-grid #grid [dataSource]="orders" height="315" width="500px" (rowSelected)="rowSelectHandler($event)" [editSettings]="editSettings">
                  <e-columns>
                    <e-column field="OrderID" headerText="OrderID" isPrimaryKey="true" textAlign="Right" width="120"></e-column>
                    <e-column field="CustomerID" headerText="CustomerID" textAlign="Right" width="120"></e-column>
                    <e-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-column>
                    <e-column field="ShipCountry" headerText="ShipCountry" textAlign="Right" width="120"></e-column>
                  </e-columns>
                </ejs-grid>
              </div>
            </div>
           `,
})
export class AppComponent {

  public orders:Object[] = data;
  @ViewChild('grid') public grid?:GridComponent;
  public dropdown: Object[] = [
    { shipCountry: 'Germany' },
    { shipCountry: 'Brazil' },
    { shipCountry: 'France' },
    { shipCountry: 'Belgium' },
    { shipCountry: 'Switzerland' },
    { shipCountry: 'Venezuela' },
    { shipCountry: 'USA' },
    { shipCountry: 'Mexico' },
    { shipCountry: 'Italy' },
    { shipCountry: 'Sweden' },
    { shipCountry: 'Finland' },
    { shipCountry: 'Spain' },
    { shipCountry: 'Canada' },
    { shipCountry: 'Portugal' },
    { shipCountry: 'Denmark' },
    { shipCountry: 'Austria' },
    { shipCountry: 'UK' },
    { shipCountry: 'Ireland' },
    { shipCountry: 'Norway' },
    { shipCountry: 'Argentina' },
  ];
  public selectedProduct: Order = new Order();
  public dropdownFields: Object = { text: 'shipCountry', value: 'shipCountry' };
  public editSettings: Object = { allowEditing: true };

  save(): void {
    const index = (this.grid as GridComponent).getSelectedRowIndexes()[0];
    (this.grid as GridComponent).updateRow(index, this.selectedProduct);
   }

  rowSelectHandler(args: RowSelectEventArgs ): void {
    (this as any).selectedProduct = { ...args.data }; 
  } 
}

export class Order {
  OrderID?: number;
  CustomerID?: string;
  Freight?: number;
  ShipCountry?: string;
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Troubleshooting: Editing works only for the first row

If editing or deleting only works for the first row in the grid, the isPrimaryKey property is likely not configured. The primary key is essential for identifying which row to edit or delete. Without it, the grid cannot distinguish between rows.

Solution: Set isPrimaryKey to true on the column that contains unique identifiers:

<e-column field='OrderID' headerText='Order ID' width='100' isPrimaryKey='true'></e-column>

Make a grid column always editable

To keep a column editable at all times, use a column template and handle input via the created event and updateRow method for user input management.

Example: Editable “Freight” column textbox template.

import { data } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { EditService, GridComponent, GridModule, PageService, parentsUntil, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';

@Component({
    imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule
        ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<ejs-grid #grid [dataSource]='data' [editSettings]='editSettings' height='315px' (created)="created()">
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' isPrimaryKey='true' width=120></e-column>
                    <e-column field='OrderDate' headerText='Order Date' width=130 textAlign='Right' format='yMd'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' width=140></e-column>
                    <e-column field='Freight' headerText='Receipt Amount' textAlign= 'Right' width=150>
                        <ng-template #template let-data>
                            <input id='' value='' class='custemp' type='text' style='width: 100%'>
                        </ng-template>
                </e-column>
                </e-columns>
                </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    @ViewChild('grid') public grid?: GridComponent;
    public editSettings?: Object;

    ngOnInit(): void {
        this.data = data;
    }
    created() {
        (this.grid as GridComponent).element.addEventListener('keyup', function (e) { // Bind the keyup event for the grid.
            if ((e.target as HTMLElement).classList.contains('custemp')) { // Based on this condition, you can find whether the target is an input element or not.
                var row = parentsUntil(e.target as HTMLElement, 'e-row');
                var rowIndex = (row as HTMLFormElement)['rowIndex']; // Get the row index.
                var uid = row.getAttribute('data-uid');
                var grid = (document.getElementsByClassName('e-grid')[0] as HTMLFormElement)['ej2_instances'][0];
                var rowData = grid.getRowObjectFromUID(uid).data; // Get the row data.
                rowData.Freight = ((e.target as HTMLFormElement)['value']); // Update the new value for the corresponding column.
                grid.updateRow(rowIndex, rowData); // Update the modified value in the row data.
            }
        });
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

If a template column has an associated field property, values from the input will be stored in that field in the data object.

See also