Batch editing in EJ2 TypeScript Grid control

29 Feb 202424 minutes to read

Batch editing is a powerful feature in the Grid control that allows you to edit multiple cells simultaneously. It provides a convenient way to make changes to multiple cells and save them in a single request to the data source. This feature is particularly useful when dealing with large datasets or when you need to update multiple cells at once.

In batch edit mode, when you double-click on a grid cell, the target cell changes to an editable state. You can perform bulk update of the added, changed, and deleted data by either clicking on the toolbar’s Update button or by externally invoking the batchSave method.

To enable batch editing mode, you need to set the editSettings->mode property to Batch. This property determines the editing mode of the Grid and allows you to activate the batch editing feature.

Here’s an example how to enable batch editing in the javascript grid control:

import { Grid, Edit, Toolbar } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar);

let grid: Grid = new Grid({
    dataSource: data,
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
    toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
    columns: [
        { field: 'OrderID', headerText: 'Order ID', validationRules: { required: true, number: true }, textAlign: 'Right', isPrimaryKey: true, width: 100 },
        { field: 'CustomerID', headerText: 'Customer ID', validationRules: { required: true }, width: 120 },
        { field: 'Freight', headerText: 'Freight', textAlign: 'Right', editType: 'numericedit', validationRules: { min: 1, max: 1000 } , width: 120, format: 'C2'},
        { field: 'ShipCountry', headerText: 'Ship Country', editType: 'dropdownedit', validationRules: { required: true }, width: 150 }
    ],
    height: 273
});
grid.appendTo('#Grid');
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id='Grid'></div>
    </div>
</body>

</html>

Automatically update the column based on another column edited value

You can automatically update the value of a column based on the edited value of another column in batch mode. This feature is useful when you want to dynamically calculate and update a column’s value in real-time based on the changes made in another related column. This can be achieved using the Cell Edit Template feature in the Grid control.

In the following example, the TotalCost column value is updated based on changes to the UnitPrice and UnitInStock columns during batch editing.

import { Grid, Edit, Toolbar, CellEditArgs,column } from '@syncfusion/ej2-grids';
import { productData } from './productData.ts';
import { NumericTextBox , ChangeEventArgs } from '@syncfusion/ej2-inputs';

Grid.Inject(Edit, Toolbar);

let priceElem: HTMLElement;;
let priceObj: NumericTextBox;
let stockElem: HTMLElement;;
let stockObj: NumericTextBox;

let grid: Grid = new Grid({
  dataSource: productData,
  cellEdit: cellEdit,
  editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
  toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
  height: 273,
  columns: [
    { field: 'ProductID', headerText: 'Product ID', textAlign: 'Right', isPrimaryKey: true, width: 100 },
    { field: 'ProductName', headerText: 'Product Name', width: 120 },
    { field: 'UnitPrice', headerText: 'UnitPrice', textAlign: 'Right',
      edit: {
        create: function() {
          priceElem = document.createElement('input');
          return priceElem;
        },
        read: function() {
          return priceObj.value;
        },
        destroy: function() {
          priceObj.destroy();
        },
        write: function(args: CellEditArgs) {
          let rowData = args.rowData;
          let rowIndex = (grid.getRowInfo(args.row) as any).rowIndex;
          priceObj = new NumericTextBox({
            value: args.rowData[(args.column as column).field],
            change: function (args: ChangeEventArgs) {
              let totalCostValue = args.value * rowData['UnitsInStock'];
              grid.updateCell(rowIndex, 'TotalCost', totalCostValue);
            }
          });
          priceObj.appendTo(priceElem);
        }
      },
      width: 150, format: 'C2' },
    { field: 'UnitsInStock', headerText: 'Units In Stock', textAlign: 'Right',
      edit: {
        create: function() {
          stockElem = document.createElement('input');
          return stockElem;
        },
        read: function() {
          return stockObj.value;
        },
        destroy: function() {
          stockObj.destroy();
        },
        write: function(args: CellEditArgs) {
          let rowData = args.rowData;
          let rowIndex = (grid.getRowInfo(args.row) as any).rowIndex;
          stockObj = new NumericTextBox({
            value: args.rowData[(args.column as column).field],
            change: function (args: ChangeEventArgs) {
              let totalCostValue = args.value * rowData['UnitPrice'];
              grid.updateCell(rowIndex, 'TotalCost', totalCostValue);
            }
          });
          stockObj.appendTo(stockElem);
        }
      },
      width: 150
    },
    { field: 'TotalCost', headerText: 'Total Unit Cost', width: 150,format: 'C2', textAlign: 'Right'}
  ]
});
grid.appendTo('#Grid');

function cellEdit(args: CellEditArgs) {
  if(args.columnName == "TotalCost") {
    args.cancel = true;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id='Grid'></div>
    </div>
</body>
</html>

  • You can utilize the updateCell method to update cells in batch mode.

Cancel edit based on condition

The Grid control provides to cancel the CRUD operations (Edit, Add, Delete) for particular row or cell in batch edit mode based on specific conditions. This feature allows you to control over whether editing should be allowed or prevented for certain rows or cells in the grid.

To cancel the edit action, you need to handle the cellEdit event. This event is triggered when a cell enters the edit mode. Within the event handler, you can add a condition to check whether the edit operation should be allowed or canceled. If the condition is met, set the args.cancel property to true to cancel the edit operation.

To cancel the add action, you need to handle the beforeBatchAdd event. This event is triggered before a new record is added to the batch changes. Within the event handler, you can add a condition to determine whether the add operation should proceed or be canceled. If the condition is met, set the args.cancel property to true to cancel the add operation.

To cancel the delete action, you need to handle the beforeBatchDelete event. This event is triggered before a record is deleted from the batch changes. Within the event handler, you can add a condition to control whether the delete operation should take place or be canceled. If the condition is met, set the args.cancel property to true to cancel the delete operation.

In the below demo, prevent the CRUD operation based on the Role column value. If the Role Column is Admin, then edit/delete action is prevented for that row.

import { Button } from '@syncfusion/ej2-buttons';
import { Grid, Edit, Toolbar, CellEditArgs, BeforeBatchAddArgs, BeforeBatchDeleteArgs } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar);

let isAddable: boolean = true;
let grid: Grid = new Grid({
    dataSource: data,
    toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
    columns: [
      { field: 'OrderID', headerText: 'Order ID', textAlign: 'Right',validationRules: { required: true, number: true }, isPrimaryKey: true, width: 100 },
      { field: 'Role', headerText: 'Role',validationRules: {required: true }, width: 120, },
      { field: 'Freight', headerText: 'Freight', textAlign: 'Right', editType: 'numericedit',validationRules: { min:1, max:1000 }, width: 120, format: 'C2' },
      { field: 'ShipCountry', headerText: 'Ship Country',editType: 'dropdownedit', width: 150 }
    ],
    cellEdit: cellEdit,
    beforeBatchAdd: beforeBatchAdd,
    beforeBatchDelete: beforeBatchDelete,  
    height: 240
});
grid.appendTo('#Grid');

function cellEdit(args: CellEditArgs) {
  if (args.rowData['Role'] == 'Admin') {
    args.cancel = true;
  }
}
function beforeBatchAdd(args: BeforeBatchAddArgs) {
  if (!isAddable) {
    args.cancel = true;
  }
}
function beforeBatchDelete(args: BeforeBatchDeleteArgs) {
  if (args.rowData['Role'] == 'Admin') {
    args.cancel = true;
  }
}

let button: Button = new Button({
    content: 'Grid is Addable',
  });
button.appendTo('#Add');
  
(document.getElementById('Add') as HTMLElement).onclick = () => {
  button.content == 'Grid is Addable'? (button.content = 'Grid is Not Addable'): (button.content = 'Grid is Addable');
  isAddable = !isAddable;
};
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <button ejs-button style="margin-top: 10px " id="Add" cssClass="e-outline"></button>
            <br><br>
        <div id='Grid'></div>
    </div>
</body>
</html>

Adding a new row at the bottom of the grid

The grid control allows you to add a new row at the bottom of the grid, allowing you to insert a new record at the end of the existing data set. This feature is particularly useful when you want to conveniently add new records without the need to scroll up or manually reposition the newly added row. To achieve this, you can make use of the newRowPosition property in the editSettings configuration and set it to Bottom.

  • If you set newRowPosition as Bottom, you can use the TAB key to easily move between cells or rows in edit mode. As you enter data in each cell and press TAB, the grid will automatically create new rows below the current row, allowing you to conveniently add data for multiple rows without having to leave the edit mode.
  • If you set newRowPosition as Top, the grid will display a blank row form at the top by default, allowing you to enter data for the new record. However, when the data is saved or updated, it will be inserted at the bottom of the grid ,ensuring the new record appears at the end of the existing data set.
  • If the paging feature is enabled, updating the row will automatically move it to the last page based on the page size.This behavior applies to both local and remote data binding.
  • If scrolling is enabled, you can use the TAB key to add a new row, even if the new row is added beyond the currently visible area of the grid.
  • Add newRowPosition is supported for Normal and Batch editing modes.

Here’s an example that demonstrates how to enable adding new rows at the bottom of the grid using newRowPosition property:

import { DropDownList, ChangeEventArgs } from '@syncfusion/ej2-dropdowns';
import { Grid, Edit, Toolbar,EditSettingsModel } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar);

let grid: Grid = new Grid({
  dataSource: data,
  editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
  allowPaging: true,
  toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
  columns: [
      { field: 'OrderID', isPrimaryKey: true, headerText: 'Order ID', textAlign: 'Right', validationRules: { required: true, number: true }, width: 100 },
      { field: 'CustomerID', headerText: 'Customer ID', validationRules: { required: true }, width: 120 },
      { field: 'Freight', headerText: 'Freight', textAlign: 'Right', editType: 'numericedit', width: 120, format: 'C2', validationRules: { min: 1, max: 1000 } },
      { field: 'OrderDate', headerText: 'Order Date', editType: 'datepickeredit', format: 'yMd', width: 130 },
      { field: 'ShipCountry', headerText: 'Ship Country', editType: 'dropdownedit', width: 150, edit: { params: { popupHeight: '300px' } } }
  ],
  height: 240
});
grid.appendTo('#Grid');

let dropdownData = [
  { text: 'Top', value: 'Top' },
  { text: 'Bottom', value: 'Bottom' }
];

let dropDown: DropDownList = new DropDownList({
  value: 'Top',
  popupHeight: '240px',
  width: 100,
  dataSource: dropdownData,
  change: change,
});
dropDown.appendTo('#dropdown');

function change(args: ChangeEventArgs) {
  (grid.editSettings as EditSettingsModel).newRowPosition = args.value;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div style="padding-bottom: 10px">
            <label 30px 17px 0 0>Select new row position: :</label>
            <input type="text" tabindex="1" id="dropdown" />
        </div>
        <div id='Grid'></div>
    </div>
</body>
</html>

Confirmation dialog

Displaying a confirmation dialog provides an additional layer of confirmation when performing actions like saving a record or canceling changes in the grid. This dialog prompts for confirmation before proceeding with the action, ensuring that accidental or undesired changes are avoided. The grid control offers a built-in confirmation dialog that can be used to confirm save, cancel, and other actions.

To enable the confirmation dialog, you can set the editSettings->showConfirmDialog property of the editSettings configuration to true. The default value is true.

  • editSettings.showConfirmDialog requires the editSettings->mode to be Batch
  • If editSettings.showConfirmDialog set to false, then confirmation dialog does not display in batch editing.
  • While performing both update and delete operations, a separate delete confirmation dialog is shown at the time of clicking the delete button or pressing the delete key itself.

Here’s an example that demonstrates how to enable/disable the confirmation dialog using the showConfirmDialog property:

import { Switch } from '@syncfusion/ej2-buttons';
import { Grid, Edit, Toolbar, EditSettingsModel } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar);

let grid: Grid = new Grid({
  dataSource: data,
  toolbar: ['Add','Delete', 'Update', 'Cancel'],
  editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch'},
  columns: [
      { field: 'OrderID', headerText: 'Order ID', textAlign: 'Right',validationRules: { required: true, number: true }, isPrimaryKey: true, width: 100 },
      { field: 'CustomerID', headerText: 'Customer ID',validationRules: { required: true },width: 120  },
      { field: 'Freight', headerText: 'Freight', textAlign: 'Right', editType: 'numericedit', width: 120, format: 'C2',validationRules: { min: 1, max: 1000 } },
      { field: 'ShipCountry', headerText: 'Ship Country', editType: 'dropdownedit', width: 150 }
  ],
  height: 273
});
grid.appendTo('#Grid');

let toggle: Switch = new Switch({
  change: toggleShowConfirmDialog,
  checked: true,
});
toggle.appendTo('#switch');

function toggleShowConfirmDialog()
{
  (grid.editSettings as EditSettingsModel).showConfirmDialog = toggle.checked;
}

let toggleDelete: Switch = new Switch({
  change: toggleShowDeleteConfirmDialog,
});
toggleDelete.appendTo('#switchDelete');

function toggleShowDeleteConfirmDialog()
{
  (grid.editSettings as EditSettingsModel).showDeleteConfirmDialog = toggleDelete.checked;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div>
            <label style="padding: 10px 10px">Enable/Disable show confirmation dialog: </label>
            <input type="checkbox" id="switch">
          </div><br><br>
          <div>
            <label style="padding: 10px 10px">Enable/Disable show delete confirmation dialog: </label>
            <input type="checkbox" id="switchDelete">
          </div>
        <div id='Grid'></div>
    </div>
</body>
</html>

How to make editing in single click and arrow keys

You can enable editing in a single click and navigate between cells or rows using arrow keys without having to double-click or use the mouse for navigation. By default, in batch mode, the TAB key can be used to edit or move to the next cell or row and the Enter key is used to move to the next row cell. However, you can customize this behavior to enable editing with a single click or using arrow keys.

To enable editing in a single click, you can handle the created event of the Grid. Within the event handler,bind the click event to the grid cells and call the editCell method to make the clicked cell editable.

To enable editing using arrow keys, you can handle the load event of the Grid control. Inside the event handler, you can bind the keydown event to the grid element and check for arrow key presses. Based on the arrow key pressed, you can identify the next or previous cell using the editCell method and make it editable.

Here’s an example that demonstrates how to achieve both single-click editing and arrow key navigation using the created and load events in conjunction with the editCell method:

import { Grid, Page, Toolbar, Edit, ClickEventArgs, KeyboardEventArgs } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';
import { isNullOrUndefined } from '@syncfusion/ej2-base';

Grid.Inject(Page, Toolbar, Edit);

let grid: Grid = new Grid({
    dataSource: data,
    allowPaging: true,
    enableHover: false,
    created: created,
    load: load,
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch'},
    toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
    columns: [
        { field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', isPrimaryKey: true, validationRules: { required: true, number: true }, width: 120},
        { field: 'CustomerID', headerText: 'Customer ID', width: 120, validationRules: { required: true }},
        { field: 'Freight', headerText: 'Freight', format: 'C2', width: 150, textAlign: 'Right',validationRules: { min:1,max:1000} },
        { field: 'OrderDate', headerText: 'Order Date', editType: 'datepickeredit', format: 'yMd', width: 150 },
        { field: 'ShipCountry', headerText: 'Ship Country', width: 150 }
    ],
    height: 272
});
grid.appendTo('#Grid');

function created() {
    grid.getContentTable().addEventListener('click', function(args: ClickEventArgs) {
        if ((args.target as HTMLElement).classList.contains('e-rowcell')) {
            grid.editModule.editCell(parseInt((args.target as HTMLElement).getAttribute('index') as string),
              grid.getColumnByIndex(parseInt((args.target as HTMLElement).getAttribute('data-colindex') as string)).field);
        }
    });
}

function load() {
    grid.element.addEventListener('keydown', function(e: KeyboardEventArgs) {
        let closesttd = (e.target as HTMLElement).closest('td');
        if (e.keyCode === 39 && !isNullOrUndefined((closesttd as HTMLTableCellElement).nextSibling as HTMLElement)) {
            editACell((closesttd as HTMLTableCellElement).nextSibling as HTMLElement);
        }
        if (e.keyCode === 37 && !isNullOrUndefined((closesttd as HTMLTableCellElement).previousSibling as HTMLElement) &&
            !grid.getColumnByIndex(
                parseInt(((closesttd as HTMLTableCellElement).previousSibling as HTMLElement).getAttribute('data-colindex') as string)).isPrimaryKey)
        {
             editACell((closesttd as HTMLTableCellElement).previousSibling as HTMLElement);
        }
        if (e.keyCode === 40 && !isNullOrUndefined(((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).nextSibling)) {
            editACell(
                (((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).nextSibling as HTMLElement).querySelectorAll('td')[
                parseInt((closesttd as HTMLTableCellElement).getAttribute('data-colindex') as string)]);
        }
        if ( e.keyCode === 38 && !isNullOrUndefined(((closesttd as HTMLTableCellElement).closest('tr')as HTMLTableRowElement).previousSibling)) {
            editACell(
                (((closesttd as HTMLTableCellElement).closest('tr') as HTMLTableRowElement).previousSibling as HTMLElement).querySelectorAll('td')[
                 parseInt((closesttd as HTMLTableCellElement).getAttribute('data-colindex')as string)]);
        }
    });
}

function editACell(args: HTMLElement) {
    grid.editModule.editCell(
        parseInt(args.getAttribute('index') as string),
        grid.getColumnByIndex(parseInt(args.getAttribute('data-colindex') as string)).field);
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id='Grid'></div>
    </div>
</body>
</html>

Disable editing for a particular cell

You can prevent editing of specific cells based on certain conditions in the Grid control. This feature is useful when you want to restrict editing for certain cells, such as read-only data, calculated values, or protected information. It helps maintain data integrity and ensures that only authorized changes can be made in the grid.

To disable editing for a particular cell in batch mode, use the cellEdit event of the grid. You can then use the args.cancel property and set it to true to prevent editing for that cell.

Here’s an example demonstrating how you can disable editing for cells containing the value France using the cellEdit event:

import { Grid, Edit, Toolbar, CellEditArgs } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar);

let grid: Grid = new Grid({
    dataSource: data,
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch'},
    toolbar: ['Add', 'Delete', 'Update', 'Cancel'],
    cellEdit: cellEdit,
    columns: [
        { field: 'OrderID', headerText: 'Order ID',validationRules: { required: true, number: true }, textAlign: 'Right', isPrimaryKey: true, width: 100 },
        { field: 'CustomerID', headerText: 'Customer ID', validationRules: { required: true }, width: 120},
        { field: 'Freight', headerText: 'Freight', textAlign: 'Right',validationRules: { min:1, max:1000 }, width: 120, format: 'C2'},
        { field: 'ShipCountry', headerText: 'Ship Country', editType: 'dropdownedit', width: 150 }
    ],
    height: 273
});
grid.appendTo('#Grid');

function cellEdit(args: CellEditArgs)
{
    if (args.value === 'France') {
      args.cancel = true;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id='Grid'></div>
    </div>
</body>
</html>

Save or update the changes immediately

The Grid control provides a convenient way to save or update changes immediately in batch mode without the need for a separate Save button. This feature is particularly useful when you want to allow you to edit data efficiently without having to manually trigger a save action. You can achieve this by utilizing the cellSaved event and the batchSave method.

By default, when you use the batchSave method to save or update data, a confirmation dialog is displayed. This dialog prompts for confirmation before proceeding with the save or cancel action, ensuring that accidental or undesired changes are avoided.

The cellSaved event is triggered when a cell is saved in the Grid. It provides a way to perform custom logic when a cell is saved or updated.

The batchSave method is a built-in function provided by the Grid’s edit service. It is used to save multiple changes made to added, edited, and deleted records in the batch mode.

  • To avoid the confirmation dialog when using the batchSave method, you can set editSettings.showConfirmDialog to false. However, please note that to use this property, the editSettings.mode must be set to Batch. This combination of properties allows you to save or update changes immediately without the need for a confirmation dialog.

Here’s an example that demonstrates how to achieve immediate saving or updating of changes using the cellSaved event and the batchSave method:

import { Grid, Edit, Toolbar,Page,EditSettingsModel  } from '@syncfusion/ej2-grids';
import { data } from './datasource.ts';

Grid.Inject(Edit, Toolbar,Page );

let grid: Grid = new Grid({
  dataSource: data,
  allowPaging: true,
  pageSetting: {pageCount: 5},
  editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, showConfirmDialog:false, mode: 'Batch'},
  toolbar: ['Add', 'Delete'],
  cellSaved: save,
  columns: [
      { field: 'OrderID', headerText: 'Order ID', width: 140, textAlign: 'Right', isPrimaryKey: true, validationRules: { required: true, number: true } },
      { field: 'CustomerID', headerText: 'Customer ID', width: 140, validationRules: { required: true }},
      { field: 'Freight', headerText: 'Freight', width: 140, format: 'C2', textAlign: 'Right',validationRules: { min:1, max:1000 },editType: 'numericedit'},
      { field: 'OrderDate', headerText: 'Order Date', width: 120, editType: 'datepickeredit', format: { type: 'dateTime', format: 'M/d/y hh:mm a' }, textAlign: 'Right'},
      { field: 'ShipCountry', headerText: 'Ship Country', width: 150, editType: 'dropdownedit',edit:{ popupHeight: '300px' } },
  ],
  height: 272
});
grid.appendTo('#Grid');

function save() {
  (grid.editModule as EditSettingsModel).batchSave();
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Grid</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript Grid Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-richtexteditor/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-notifications/styles/bootstrap5.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/bootstrap5.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id='Grid'></div>
    </div>
</body>
</html>