HelpBot Assistant

How can I help you?

Dialog Editing in Angular Grid Component

19 Mar 202624 minutes to read

The Syncfusion® Angular Grid Component dialog editing provides a powerful, efficient way to edit row data through a dedicated modal dialog window that focuses attention on the editing form. Instead of editing cells directly in the grid, multiple field values can be entered and modified at once in a clean, organized form. The grid automatically saves all changes to the data source without navigating away from the current page making data entry faster, more intuitive, and less error-prone, especially when dealing with complex records that span multiple columns.

To enable dialog editing in the grid component, set the editSettings.mode property to Dialog. This property determines the editing mode for the grid.

The following example demonstrates to enable dialog 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' 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 {

    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, mode: 'Dialog' };
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true, number: true };
        this.customerIDRules = { required: true };
        this.freightRules = { min: 1, max: 1000 };
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Customize the edit dialog

The edit dialog is a modal window that isolates the editing form and prevents grid interaction until the dialog closes. Customize this dialog to match application needs by handling the actionComplete event.

Customizable options:

  • Header text, close button visibility, and height.
  • Button text and localization strings.
  • Dialog behavior based on the editing action.

Use the requestType parameter to identify which action triggered the event and apply appropriate customizations:

Request Type Description
beginEdit Editing an existing record
add Creating a new record
save Updating a new or existing record
delete Deleting an existing record

Refer to the Grid Default text list for localization options.

The following example demonstrates to customize the edit dialog using the actionComplete event:

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

L10n.load({
    'en-US': {
        grid: {
            'SaveButton': 'Submit',
            'CancelButton': 'Discard'
        }
    }
});

@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'
               (actionComplete)="actionComplete($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' [validationRules]='customerIDRules' headerText='Customer ID' width=120></e-column>
                    <e-column field='Freight' [validationRules]='freightRules' 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 {

    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, mode: 'Dialog' };
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
        this.orderIDRules = { required: true, number: true };
        this.customerIDRules = { required: true };
        this.freightRules = { min: 1, max: 1000 };
    }
    actionComplete(args: EditEventArgs) {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            const dialog = (args as any).dialog;
            dialog.showCloseIcon = false;
            dialog.height = 300;
            dialog.width = 300;
            // change the header of the dialog
            dialog.header = args.requestType === 'beginEdit' ? 'Edit Record of ' + (args.rowData as columnDataType)['CustomerID'] : 'New Customer';
        }
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

The Grid’s add or edit dialog element applies a max-height property that is calculated based on the available window height. For typical screens (1920 x 1080), the dialog’s maximum height can be set up to 658px.

Show or hide columns in dialog editing

Column visibility can be dynamically controlled in dialog editing mode. The actionBegin event enables showing or hiding specific columns based on whether an existing record is being edited or a new record is being added.

The actionBegin event is triggered whenever an action is initiated in the grid (editing, adding, or deleting a record). Within the event requestType parameter determines to manage column visibility in the event handler:

Request Type Action Column Visibility Control
beginEdit or add Editing or creating a record Modify visibility using the visible property
save Saving the record Reset columns to initial visibility state using visible property

In the following example, the “Customer ID” column is rendered as a hidden column, and the “Ship Country” column is rendered as a visible column. In the edit mode, the “Customer ID” column will be changed to a visible state and the “Ship Country” column will be changed to a hidden state.

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 { Column, EditEventArgs, EditService, EditSettingsModel, GridComponent, 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
    ],
    providers: [EditService, ToolbarService, SortService, PageService],
    standalone: true,
    selector: 'app-root',
    template: `<ejs-grid #grid [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar' (actionBegin)="actionBegin($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' [validationRules]='customerIDRules' 
                    headerText='Customer ID' [visible]='false' 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[];
    @ViewChild('grid') grid?: GridComponent;
    public orderIDRules?: Object;
    public customerIDRules?: Object;
    public freightRules?: Object;

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

    actionBegin(args: EditEventArgs) {
        if (args.requestType === 'beginEdit') {
            for (const cols of (this.grid as GridComponent).columns) {
                if ((cols as Column).field === 'CustomerID') {
                    (cols as Column).visible = true;
                } else if ((cols as Column).field === 'ShipCountry') {
                    (cols as Column).visible = false;
                }
            }
        }
        else if (args.requestType === 'add') {
            for (const cols of (this.grid as GridComponent).columns) {
                if ((cols as Column).field === 'CustomerID') {
                    (cols as Column).visible = true;
                }
            }
        }
        else if (args.requestType === 'save') {
            for (const cols of (this.grid as GridComponent).columns) {
                if ((cols as Column).field === 'CustomerID') {
                    (cols as Column).visible = false;
                } else if ((cols as Column).field === 'ShipCountry') {
                    (cols as Column).visible = true;
                }
            }
        }
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Wizard-like dialog editing

Wizard-like dialog editing divides complex forms into manageable step-by-step sections with efficient navigation. The editSettings.template property enables custom form templates with multiple steps:

  1. Set editSettings.mode to Dialog.
  2. Define template sections for each form step.
  3. Add navigation buttons (Previous, Next, Save) between steps.
  4. Implement validation for each step.

The example below demonstrates the wizard-like dialog editing in the grid using unobtrusive validation:

import { data } from './datasource';
import { CommonModule } from '@angular/common';
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ButtonModule, CheckBoxAllModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns';
import { DialogEditEventArgs, EditService, EditSettingsModel, GridComponent, GridModule, ToolbarItems, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { NumericTextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { DataUtil } from '@syncfusion/ej2-data';

@Component({
  imports: [
      CommonModule,
      CheckBoxAllModule,
      GridModule,
      ButtonModule,
      DropDownListAllModule, 
      ReactiveFormsModule, 
      FormsModule,
      NumericTextBoxModule
      ],
  providers: [EditService, ToolbarService],
  standalone: true,
  selector: 'app-root',
  templateUrl: `wizardtemplate.html`
})
export class AppComponent implements OnInit {

  public data?: object[];
  public editSettings?: EditSettingsModel;
  public toolbar?: ToolbarItems[];
  public shipCountryDistinctData?: object;
  public shipCityDistinctData: object[] = [];
  public currentTab = 0;
  @ViewChild('grid') grid?: GridComponent;
  @ViewChild('orderForm') orderForm?: FormGroup;

  ngOnInit(): void {
    this.data = data;
    this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
    this.toolbar = ['Add', 'Edit', 'Delete'];
    this.shipCountryDistinctData = DataUtil.distinct(data, 'ShipCountry', true);
    this.shipCityDistinctData = DataUtil.distinct(data, 'ShipCity', true);

  }

  actionComplete(args: DialogEditEventArgs) {
    if ((args.requestType === 'beginEdit') || (args.requestType === 'add')) {
      (args as any).form.ej2_instances[0].rules = {}; // Disable default validation.
      (args.dialog as any).element.classList.add('hide-default-buttons');
      // Set initial Focus
      if (args.requestType === 'beginEdit') {
        (args?.form?.elements.namedItem('CustomerID') as HTMLInputElement).focus();
      }
      this.currentTab = 0;
    }
  }
  saveBtn() {
    if (this.orderForm?.valid) {
      (this.grid as GridComponent).endEdit();
    }
  }

  nextBtn() {
    if (this.orderForm?.valid) {
      this.currentTab++;
      this.removeFocusFromButton()
    }
  }

  previousBtn() {
    if (this.orderForm?.valid) {
      this.currentTab--;
      this.removeFocusFromButton()
    }
  }

  removeFocusFromButton() {
    const nextButton = document.getElementById('btn') as HTMLButtonElement;
    if (nextButton) {
      nextButton.blur();
    }
  }
}
<ejs-grid #grid [dataSource]="data" allowPaging="true" [editSettings]="editSettings" [toolbar]="toolbar"
  (actionComplete)="actionComplete($event)" height='273px'>
  <e-columns>
    <e-column field="OrderID" headerText="Order ID" width="120" textAlign="Right" isPrimaryKey="true"></e-column>
    <e-column field="CustomerID" headerText="Customer Name" width="120"></e-column>
    <e-column field="Freight" headerText="Freight" width="120"></e-column>
    <e-column field="ShipCity" headerText="Ship City" width="120"></e-column>
    <e-column field="ShipCountry" headerText="Ship Country" width="150"></e-column>
    <e-column field="Verified" headerText="Verified" width="100" type="boolean" [displayAsCheckBox]="true">
    </e-column>
  </e-columns>
  <ng-template #editSettingsTemplate let-data>
    <div ngForm #orderForm="ngForm">
      <div id="tab0" class="tab" [hidden]="currentTab !== 0">
        <!-- Tab 0 Content -->
        <div class="form-row">
          <div class="form-group col-md-6">
            <div class="e-float-input e-control-wrapper"
              [ngClass]="{'e-error': OrderID.invalid && (OrderID.dirty || OrderID.touched)}">
              <input [(ngModel)]="data.OrderID" required id="OrderID" name="OrderID" type="text"
                [disabled]="!data.isAdd " #OrderID="ngModel">
              <span class="e-float-line"></span>
              <label class="e-float-text e-label-top" for="OrderID"> Order ID</label>
            </div>
            <div id="OrderIDError" *ngIf="OrderID.invalid && (OrderID.dirty || OrderID.touched)">
              <label class="e-error" for="OrderID" id="OrderID-info" style="display: block;">*Order ID is
                required</label>
            </div>
          </div>
        </div>
        <div class="form-row">
          <div class="form-group col-md-6">
            <div class="e-float-input e-control-wrapper"
              [ngClass]="{'e-error': CustomerID.invalid && (CustomerID.dirty || CustomerID.touched)}">
              <input [(ngModel)]="data.CustomerID" required id="CustomerID" name="CustomerID" type="text"
                #CustomerID="ngModel">
              <span class="e-float-line"></span>
              <label class="e-float-text e-label-top" for="CustomerID">Customer Name</label>
            </div>
            <div id="CustomerIDError" *ngIf="CustomerID.invalid && (CustomerID.dirty || CustomerID.touched)">
              <label class="e-error" for="CustomerID" id="CustomerID-info" style="display: block;">*Customer Name
                is required</label>
            </div>
          </div>
        </div>
      </div>
      <div id="tab1" class="tab" [hidden]="currentTab !== 1">
        <!-- Tab 2 content -->
        <div class="form-row">
          <div class="form-group col-md-6">

            <div class="e-float-input e-control-wrapper"
              [ngClass]="{'e-error': Freight.invalid && (Freight.dirty || Freight.touched)}">
              <div>
                <label class="e-float-text e-label-top" for="Freight">Freight</label>
              </div>
              <!-- Use ejs-numerictextbox for numeric input -->
              <ejs-numerictextbox style="margin-top:10px" [(ngModel)]="data.Freight" required id="Freight"
                name="Freight" #Freight="ngModel" floatLabelType='Always'></ejs-numerictextbox>
            </div>
            <div id="FreightError" *ngIf="Freight.invalid && (Freight.dirty || Freight.touched)">
              <label class="e-error" for="Freight" id="Freight-info" style="display: block;">*Freight is
                required</label>
            </div>
          </div>
        </div>
        <div class="form-row">
          <div class="form-group col-md-6">
            <!-- Use ejs-dropdownlist for ShipCity -->
            <ejs-dropdownlist id="ShipCity" name="ShipCity" [(ngModel)]="data.ShipCity"
              [dataSource]="shipCityDistinctData" [fields]="{ text: 'ShipCity', value: 'ShipCity' }"
              placeholder="Ship City" popupHeight="300px" floatLabelType="Always"></ejs-dropdownlist>
          </div>
        </div>
      </div>


      <div id="tab2" class="tab" [hidden]="currentTab !== 2">
        <!-- Tab 2 Content -->
        <div class="form-row">
          <div class="form-group col-md-6">
            <ejs-dropdownlist id="ShipCountry" name="ShipCountry" [(ngModel)]="data.ShipCountry"
              [dataSource]="shipCountryDistinctData" [fields]="{text: 'ShipCountry', value: 'ShipCountry' }"
              placeholder="Ship Country" popupHeight="300px" floatLabelType="Always"></ejs-dropdownlist>
          </div>
        </div>
        <div class="form-row">
          <div class="form-group col-md-6">
            <ejs-checkbox #Verified name="Verified" id="Verified" label="Verified" [checked]="data.Verified">
            </ejs-checkbox>
          </div>
        </div>
      </div>
      <div id='footer'>
        <div style="float: left;">
          <button ejs-button type="button" cssClass="e-info e-btn" (click)="previousBtn()" id="btn"
            *ngIf="currentTab !== 0">Previous</button>
        </div>
        <div style="float: right;">
          <button ejs-button style="margin-right:10px" type="button" cssClass="e-info e-btn"
            (click)="saveBtn()">Save</button>
          <button ejs-button type="button" cssClass="e-info e-btn" (click)='nextBtn()' *ngIf="currentTab !== 2" id="btn"
            [disabled]="(CustomerID.invalid || OrderID.invalid)">next</button>
        </div>
      </div>

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

The dialog footer can be customized to add custom buttons or modify button appearance with effortless integration. The default footer displays Save and Cancel buttons. The actionComplete event enables adding custom buttons, changing button text, or implementing custom button actions in the dialog footer.

In the following sample, using the dialog argument of the actionComplete event, the action for the custom button can be customized.

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 { EditEventArgs, EditService, EditSettingsModel, GridComponent, 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 #grid [dataSource]='data' [editSettings]='editSettings' [toolbar]='toolbar'
               (actionComplete)="actionComplete($event)" 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' [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;
  @ViewChild('grid') public grid?: GridComponent;

  ngOnInit(): void {
    this.data = data;
    this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
    this.toolbar = ['Add', 'Edit', 'Delete'];
    this.orderIDRules = { required: true, number: true };
    this.customerIDRules = { required: true };
    this.freightRules = { min: 1, max: 1000 };
  }
  actionComplete(args: EditEventArgs) {
    if (args.requestType === 'beginEdit' || args.requestType === 'add') {
      const dialogInstance = (args as any).dialog;
      dialogInstance.buttons = [
        {
          buttonModel: { content: 'Discard', cssClass: 'e-primary custom-button-style' },
          click: () => {
            this.grid?.editModule.closeEdit();
          }
        },
        {
          buttonModel: { content: 'Submit', cssClass: 'e-success custom-button-style' },
          click: () => {
            this.grid?.editModule.endEdit();
          }
        }
      ];
    }
  }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));