HelpBot Assistant

How can I help you?

Batch Editing in Angular Grid Component

19 Mar 202624 minutes to read

Batch editing is a powerful feature in the Grid component that enables simultaneous modification and saving of multiple cells in a single action. This feature provides an efficient way to make bulk changes without saving each change individually, making it particularly useful for large datasets.

To enable batch editing mode, set the editSettings.mode property to Batch. When activated, double-clicking a cell enters edit mode, and bulk updates can be saved via the toolbar’s Update button or by invoking the batchSave method.

The following example demonstrates enabling batch editing in the Angular Grid component:

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' textAlign= 'Right'
                    editType= 'numericedit' [validationRules]='freightRules' 
                    width=120 format= 'C2'></e-column>
                    <e-column field='ShipCountry' headerText='Ship Country' 
                    editType= 'dropdownedit' [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, mode: 'Batch' };
        this.toolbar = ['Add','Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true, number: true };
        this.customerIDRules = { required: true };
        this.freightRules =  { min:1, max:1000 };
        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));

Automatically update a column based on changes in another column

Synchronize column values dynamically by calculating and updating dependent columns in real-time as related columns are edited. This seamless data synchronization is particularly valuable for computed fields like totals, discounts, or derived metrics that depend on other cell values. This can be achieved using the Cell Edit Template feature, this approach maintains data consistency without manual recalculation.

In the following example, the “Total Cost” column value is updated based on changes to the “Unit Price” and “Units In Stock” columns.

import { productData } 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, GridModule, PageService, SortService, ToolbarService , GridComponent, EditSettingsModel, ToolbarItems, IEditCell, CellEditArgs} from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { ChangeEventArgs, NumericTextBox } from '@syncfusion/ej2-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 id="grid" [dataSource]="data" [editSettings]="editSettings" [toolbar]="toolbar" 
      height="273px" (cellEdit)="cellEdit($event)">
        <e-columns>
          <e-column field="ProductID" headerText="Product ID" textAlign="Right" isPrimaryKey="true" 
          width="100"></e-column>
          <e-column field="ProductName" headerText="Product Name" width="120"></e-column>
          <e-column field="UnitPrice" headerText="Unit Price" [edit]="priceParams" width="150" 
          format="C2" textAlign="Right"></e-column>
          <e-column field="UnitsInStock" headerText="Units In Stock" [edit]="stockParams" 
          width="150" textAlign="Right"></e-column>
          <e-column field="TotalCost" headerText="Total Cost" width="150" format="C2" 
          textAlign="Right"></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 priceParams?: IEditCell;
  public stockParams?: IEditCell;
  public priceElem?: HTMLElement;
  public priceObj?: NumericTextBox;
  public stockElem?: HTMLElement;
  public stockObj?: NumericTextBox;

  ngOnInit(): void {
    this.data = productData;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
      mode: 'Batch'
    };
    this.toolbar = ['Add', 'Delete', 'Update', 'Cancel'];
    this.priceParams = {
      create: () => {
        this.priceElem = document.createElement('input');
        return this.priceElem;
      },
      read: () => {
        return this.priceObj?.value;
      },
      destroy: () => {
        this.priceObj?.destroy();
      },
      write: (args: any) => {
        var rowData = args.rowData;
        var rowIndex = (this.grid as GridComponent).getRowInfo(args.row).rowIndex;
        this.priceObj = new NumericTextBox({
          value: args.rowData[args.column.field],
          change: ((args: ChangeEventArgs) => {
            var totalCostValue = (args.value as number) * rowData['UnitsInStock'];
            (this.grid as GridComponent).updateCell((rowIndex as number), 'TotalCost', totalCostValue);
          }).bind(this)
        });
        this.priceObj?.appendTo(this.priceElem);
      }
    };
    this.stockParams = {
      create: () => {
        this.stockElem = document.createElement('input');
        return this.stockElem;
      },
      read: () => {
        return (this.stockObj as  NumericTextBox).value;
      },
      destroy: () => {
        (this.stockObj as  NumericTextBox).destroy();
      },
      write: (args: any) => {
        var rowData = args.rowData;
        var rowIndex = (this.grid as GridComponent).getRowInfo(args.row).rowIndex;
        this.stockObj = new NumericTextBox({
          value: args.rowData[args.column.field],
          change: ((args: ChangeEventArgs) => {
            var totalCostValue = (args.value as number) * rowData['UnitPrice'];
            (this.grid as GridComponent).updateCell((rowIndex as number), 'TotalCost', totalCostValue);
          }).bind(this)
        });
        this.stockObj.appendTo(this.stockElem);
      }
    };
  }
  cellEdit(args: CellEditArgs) {
    if (args.columnName == 'TotalCost') {
      args.cancel = true;
    }
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

The updateCell method enables programmatic updates to cells in batch mode.

Cancel editing based on specific conditions

Batch edit mode provides control over CRUD operations by allowing specific rows or cells to be protected from editing, adding, or deletion. This data integrity feature ensures only authorized modifications proceed.

The following table summarizes the events and cancellation methods available for each operation:

Operation Event Trigger Point Cancellation Method
Edit cellEdit When a cell enters edit mode Set args.cancel to true
Add beforeBatchAdd Before a new record is added Set args.cancel to true
Delete beforeBatchDelete Before a record is deleted Set args.cancel to true

Each event handler receives operation context, allowing condition-based logic to block or permit the action.

In the following demo, CRUD operations are prevented based on the “Role” column value. When the “Role” column contains “Admin”, edit and delete actions are prevented for that row.

import { columnDataType, data } from './datasource';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DatePickerAllModule, TimePickerModule } from '@syncfusion/ej2-angular-calendars';
import { AutoCompleteModule, MultiSelectModule } from '@syncfusion/ej2-angular-dropdowns';
import { BeforeBatchAddArgs, BeforeBatchDeleteArgs, CellEditArgs, 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,
        ButtonModule
        ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<div style="padding:0px 0px 20px 0px">
                <button ejs-button id="sample" (click)="btnClick($event)">Grid is Addable</button> 
               </div> 
               <ejs-grid  [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar' (cellEdit)="cellEdit($event)" 
               (beforeBatchAdd)="beforeBatchAdd($event)" (beforeBatchDelete)="beforeBatchDelete($event)" height='240px'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' [validationRules]='orderIDRules' 
                    isPrimaryKey='true' width=100></e-column>
                    <e-column field='Role' headerText='Role' [validationRules]='roleRules' 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 isAddable?: boolean = true;
    public orderIDRules?: Object;
    public roleRules?: Object;
    public freightRules?: Object;

    ngOnInit(): void {
        this.data = data;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' };
        this.toolbar = ['Add', 'Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true, number: true };
        this.roleRules = {required: true };
        this.freightRules =  { min:1, max:1000 };
    }
    cellEdit(args: CellEditArgs) {
        if ((args.rowData as columnDataType)['Role'] == 'Admin') {
            args.cancel = true;
        }
    }
    beforeBatchAdd(args: BeforeBatchAddArgs) {
        if (!this.isAddable) {
            args.cancel = true;
        }
    }
    beforeBatchDelete(args: BeforeBatchDeleteArgs) {
        if ((args.rowData as columnDataType)['Role'] == 'Admin') {
            args.cancel = true;
        }
    }
    btnClick(args: MouseEvent) {
        (args.target as HTMLElement).innerText == 'GRID IS ADDABLE' ? ((args.target as HTMLElement).innerText = 'Grid is Not Addable') : ((args.target as HTMLElement).innerText = 'Grid is Addable');
        this.isAddable = !this.isAddable;
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Adding a new row at the bottom of the grid

The Grid can be configured to add new rows at the bottom of the data set, making it convenient to insert new records at the end. Set the newRowPosition property in editSettings to Bottom to activate this option.

  • When newRowPosition is set to Bottom, the TAB key facilitates data entry across cells or rows while in edit mode. Entering data and pressing TAB automatically creates new rows below the current one, allowing efficient entry for multiple records.
  • When newRowPosition is set to Top, a blank row is displayed at the top for data entry. Upon saving, the new record appears at the end of the grid.
  • If paging is enabled, updating the row causes it to move to the last page, depending on page size. This behavior applies to both local and remote data sources.
  • With scrolling enabled, the TAB key allows adding new rows, even beyond the visible portion of the grid.
  • The newRowPosition property is supported in both Normal and Batch editing modes.

The following example illustrates to enable the addition of new rows at the bottom of the grid with the newRowPosition property:

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, NewRowPosition, 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',
    template: `
        <div style="display: flex">
            <label style="padding: 5px 5px 0 0;"> Select new row position:</label>
            <ejs-dropdownlist index="0" width="100" 
            [dataSource]="positionData" (change)="changePosition($event)">
            </ejs-dropdownlist>
      </div>
      <div style="padding-top:5px">
        <ejs-grid #batchgrid id='Batchgrid' [dataSource]='data' allowPaging='true' [pageSettings]='pageSettings' [editSettings]='editSettings' [toolbar]='toolbar' >
            <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('batchgrid')
    public grid?: GridComponent;
    public editSettings?: Object;
    public toolbar?: string[];
    public orderIDRules?: Object;
    public customerIDRules?: Object;
    public freightRules?: Object;
    public editparams?: Object;
    public pageSettings?: Object;
    public positionData: Object[] = [
        { text: 'Top', value: 'Top' },
        { text: 'Bottom', value: 'Bottom' },

    ];
    public ngOnInit(): void {
        this.data = data;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' };
        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 changePosition(args: ChangeEventArgs): void {
        (this.grid as GridComponent).editSettings.newRowPosition = args.value as NewRowPosition;
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Confirmation dialog

Confirmation dialogs provide an additional layer of protection when performing critical actions in batch editing mode. The Grid component offers built-in confirmation dialogs for the following scenarios:

Action Confirmation Trigger
Save Changes Appears when clicking Update button in toolbar.
Cancel Changes Appears when clicking Cancel button to discard edits.
Delete Record Appears when deleting a row during batch mode.

Enable the confirmation dialog by setting editSettings.showConfirmDialog to true in the editSettings configuration. The default value is true.

  • editSettings.showConfirmDialog requires editSettings.mode to be set to Batch.
  • Setting editSettings.showConfirmDialog to false disables the confirmation dialog in batch editing mode.
  • During updates and deletions, a dedicated delete confirmation dialog appears when selecting the delete button or pressing the delete key.

The following example demonstrates enabling or disabling the confirmation dialog using the showConfirmDialog property:

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

@Component({
  imports: [
      GridModule,
      DatePickerAllModule,
      FormsModule,
      TimePickerModule,
      FormsModule,
      TextBoxModule,
      MultiSelectModule,
      AutoCompleteModule,
      SwitchModule
      ],
  providers: [EditService, ToolbarService, SortService, PageService],
  standalone: true,
  selector: 'app-root',
  template: `
      <div style="padding: 5px">
        <label>Enable/Disable show confirmation dialog</label>
        <ejs-switch id="switch" [(checked)]="enableShowConfirmDialog" 
        (change)="toggleShowConfirmDialog($event)"></ejs-switch>
      </div>
      <div style="padding: 5px">
        <label>Enable/Disable show delete confirmation dialog</label>
        <ejs-switch id="switch1" [(checked)]="enableShowDeleteConfirmDialog" 
        (change)="toggleShowDeleteConfirmDialog($event)"></ejs-switch>
      </div>
      <ejs-grid #grid [dataSource]="data" [editSettings]="editSettings" [toolbar]="toolbar" height="220px">
        <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" format="C2"
            [validationRules]="freightRules"></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[] = ['Add', 'Delete', 'Update', 'Cancel'];
  public orderIDRules: Object = {};
  public customerIDRules: Object = {};
  public freightRules: Object = {};
  public enableShowConfirmDialog: boolean = true;
  public enableShowDeleteConfirmDialog: boolean = false;

  ngOnInit(): void {
    this.data = data;
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
      mode: 'Batch'
    };
    this.orderIDRules = { required: true, number: true };
    this.customerIDRules = { required: true };
    this.freightRules = { min: 1, max: 1000 };
  }
  toggleShowConfirmDialog(args:ChangeEventArgs): void {
    (this.grid as GridComponent).editSettings.showConfirmDialog = this.enableShowConfirmDialog;
  }
  toggleShowDeleteConfirmDialog(args:ChangeEventArgs): void {
    (this.grid as GridComponent).editSettings.showDeleteConfirmDialog = this.enableShowDeleteConfirmDialog;
  }
  
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Single-click editing and navigation with arrow keys

The Grid can be configured for single-click cell editing and navigation across cells or rows using arrow keys, streamlining data entry without requiring double-clicks or mouse navigation.

By default, batch edit mode enables the TAB key to move between cells/rows for editing, while the Enter key moves to the adjacent row cell. To enable single-click editing, handle the created event and bind a click event to grid cells that calls the editCell method on click.

For arrow key navigation, handle the load event and bind a keydown event to the grid element. Respond to arrow key presses by determining the direction and programmatically invoking the editCell method for the intended cell.

Below is an example showing both single-click editing and arrow key navigation by using the created and load events alongside the editCell method:

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, EditSettingsModel, GridComponent, GridModule, PageService, SortService, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

@Component({
    imports: [
        GridModule,
        DatePickerAllModule,
        FormsModule,
        TimePickerModule,
        FormsModule,
        TextBoxModule,
        MultiSelectModule,
        AutoCompleteModule
    ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<ejs-grid  #grid id="grid" [dataSource]='data' height='220px' [allowPaging]="true" [enableHover]="false" [editSettings]='editSettings' [toolbar]='toolbar'
    (created)="created()" (load)="load()">
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right'
                     isPrimaryKey='true' [validationRules]='orderIDrules' visible='false' width=120></e-column>
                    <e-column field='CustomerID' [validationRules]='customerIDrules' headerText='Customer ID' width=120></e-column>
                    <e-column field='Freight' headerText='Freight' [validationRules]='freightrules' format='C2' textAlign='Right' width=150></e-column>
                    <e-column field='OrderDate' headerText='Order Date' editType='datepickeredit' [validationRules]='orderDaterules' format='yMd' width=150></e-column>
                    <e-column field='ShipCountry' [validationRules]='shipCountryrules' headerText='Ship Country' width=150></e-column>
                </e-columns>
                </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbar?: ToolbarItems[];
    @ViewChild('grid')
    public grid?: GridComponent;
    public orderIDrules?: Object;
    public customerIDrules?: Object;
    public freightrules?: Object;
    public shipCountryrules?: Object;
    public orderDaterules?: Object;

    ngOnInit(): void {
        this.data = data;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
        this.toolbar = ['Add', 'Delete', 'Update', 'Cancel'];
        this.orderIDrules = { required: true, number: true };
        this.customerIDrules = { required: true };
        this.freightrules = { min: 1, max: 1000 };
        this.shipCountryrules = { required: true };
        this.orderDaterules = { required: true };
    }
    created = () => {
        (this.grid as GridComponent).getContentTable().addEventListener('click', (args) => {
            if ((args.target as HTMLElement).classList.contains('e-rowcell')) {
                (this.grid as GridComponent).editModule.editCell(parseInt(((args.target as HTMLElement).getAttribute('index') as string)),
                    (this.grid as GridComponent).getColumnByIndex(parseInt((args.target as HTMLElement).getAttribute('aria-colindex') as string) - 1).field);
            }
        });
    };
    load = () => {
        (this.grid as GridComponent).element.addEventListener('keydown', (e) => {
            var closesttd = (e.target as HTMLElement).closest('td');
            if (e.keyCode === 39 && !isNullOrUndefined(((closesttd as HTMLTableCellElement).nextSibling as HTMLElement))) {
                this.editACell(((closesttd as HTMLTableCellElement).nextSibling as HTMLElement));
            }
            if (e.keyCode === 37 && !isNullOrUndefined(((closesttd as HTMLTableCellElement).previousSibling as HTMLElement)) &&
                !(this.grid as GridComponent).getColumnByIndex(
                    parseInt((((closesttd as HTMLTableCellElement).previousSibling as HTMLElement).getAttribute('aria-colindex') as string)) - 1).isPrimaryKey) {
                this.editACell(((closesttd as HTMLTableCellElement).previousSibling as HTMLElement));
            }
            if (e.keyCode === 40 && !isNullOrUndefined((((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).nextSibling as HTMLElement))) {
                this.editACell(
                    (((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).nextSibling as HTMLElement).querySelectorAll('td')[
                    parseInt(((closesttd as HTMLTableCellElement).getAttribute('aria-colindex') as string)) - 1]);
            }
            if (e.keyCode === 38 && !isNullOrUndefined((((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).previousSibling as ChildNode))) {
                this.editACell(
                    (((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).previousSibling as HTMLElement).querySelectorAll('td')[
                    parseInt(((closesttd as HTMLTableCellElement).getAttribute('aria-colindex') as string)) - 1]);
            }
        });
    };
    public editACell(args: HTMLElement) {
        (this.grid as GridComponent).editModule.editCell(
            parseInt((args.getAttribute('index') as string)),
            (this.grid as GridComponent).getColumnByIndex(parseInt(args.getAttribute('aria-colindex') as string) - 1).field);
    }
}
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 cells

Editing can be restricted for designated cells based on custom criteria, enhancing data integrity by preventing changes to read-only, calculated, or protected information.

To disable editing for specific cells in batch mode, utilize the cellEdit event in the grid. By setting args.cancel to true within the event handler, editing is prevented for the target cell.

For example, the following illustrates disabling editing for cells with the value “France” using the cellEdit event:

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 { CellEditArgs, 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' (cellEdit)="cellEdit($event)" 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' textAlign= 'Right'
                    editType= 'numericedit' [validationRules]='freightRules' 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 {

    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 = { allowAdding: true, allowEditing: true,allowDeleting:true, mode: 'Batch' };
        this.toolbar = ['Add','Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true };
        this.customerIDRules = { required: true };
        this.freightRules =  { min:1, max:1000 };
    }
    cellEdit(args: CellEditArgs) {
        if (args.value === 'France') {
            args.cancel = true;
        }
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Immediate save or update of changes

The Grid enables immediate saving or updating of batch mode changes, removing the necessity for a separate Save button. This supports efficient data editing workflows and instant feedback upon cell modification. The cellSaved event, in conjunction with the batchSave method, facilitates this approach.

By default, invoking the batchSave method displays a confirmation dialog. This dialog is used to verify the correctness of the changes being saved or canceled.

The cellSaved event triggers after saving a cell, allowing execution of custom logic upon cell save or update.

The batchSave method, provided by the grid’s edit service, commits all added, edited, and deleted records in batch mode.

To bypass the confirmation dialog when using batchSave, set editSettings.showConfirmDialog to false. Note that editSettings.mode must be configured as Batch for this property to take effect. This combination enables immediate persistence of changes without confirmation.

The following example demonstrates immediate saving or updating using the cellSaved event and the batchSave method:

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 { CellSaveArgs, EditService, GridComponent, GridModule, PageService, 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',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {

  @ViewChild('normalgrid')
  public grid?: GridComponent;
  public data?: Object[];
  public editSettings?: Object;
  public toolbar?: string[];
  public orderIDRules?: Object;
  public customerIDRules?: Object;
  public freightRules?: Object;
  public editparams?: Object;
  public pageSettings?: Object;
  public formatoptions?: Object;

  public ngOnInit(): void {
    this.data = data.slice(0, 15);
    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
      showConfirmDialog:false,
      mode: 'Batch',
    };
    this.toolbar = ['Add', 'Delete'];
    this.orderIDRules = { required: true, number: true };
    this.customerIDRules = { required: true };
    this.freightRules = { required: true };
    this.editparams = { params: { popupHeight: '300px' } };
    this.pageSettings = { pageCount: 5 };
    this.formatoptions = { type: 'dateTime', format: 'M/d/y hh:mm a' };
  }
  save(args: CellSaveArgs) {
    this.grid?.editModule.batchSave();
  }
}
<div class="control-section" >
    <div class="col-lg-9">
      <ejs-grid #normalgrid id="Normalgrid" height='220px' [dataSource]="data" allowPaging="true" [pageSettings]="pageSettings"
      [editSettings]="editSettings" [toolbar]="toolbar" (cellSaved)="save($event)">
        <e-columns>
          <e-column field="OrderID" headerText="Order ID" width="140" textAlign="Right" isPrimaryKey="true"
          [validationRules]="orderIDRules"></e-column>
          <e-column field="CustomerID" headerText="Customer ID" width="140"
          [validationRules]="customerIDRules"></e-column>
          <e-column field="Freight" headerText="Freight" width="140" format="C2" textAlign="Right"
          editType="numericedit" [validationRules]="freightRules"></e-column>
          <e-column field="OrderDate" headerText="Order Date" width="120" editType="datetimepickeredit"
          [format]="formatoptions" textAlign="Right"></e-column>
          <e-column field='ShipCountry' headerText='Ship Country' width='150' editType='dropdownedit' 
          [edit]='editparams'></e-column>
        </e-columns>
      </ejs-grid>
    </div>
  </div>
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));