Dialog editing in Angular TreeGrid component

15 May 202424 minutes to read

Dialog editing is a feature in the TreeGrid component that allows you to edit the data of the currently selected row using a dialog window. With dialog editing, you can easily modify cell values and save the changes back to the data source. This feature is particularly beneficial in scenarios where you need to quickly modify data without navigating to a separate page or view, and it streamlines the process of editing multiple cells.

To enable dialog editing in tree grid, you need to set the editSettings.mode property to Dialog. This property determines the editing mode for the tree grid, and when set to Dialog, it enables the dialog editing feature.

Here’s an example how to enable dialog editing in the tree grid:

import { NgModule,ViewChild } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { PageService, SortService, FilterService,EditService,ToolbarService } from '@syncfusion/ej2-angular-treegrid'

import { Component, OnInit } from '@angular/core';
import { sampleData } from './datasource';
import { EditSettingsModel, ToolbarItems } from '@syncfusion/ej2-angular-treegrid';

@Component({
    imports: [ TreeGridModule  ],

    providers: [PageService,
                SortService,
                FilterService,
                EditService,
                ToolbarService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid [dataSource]='data'  [toolbar]='toolbarOptions' [treeColumnIndex]='1' height='270' [editSettings]='editSettings' childMapping='subtasks' >
                    <e-columns>
                        <e-column field='taskID' headerText='Task ID' [isPrimaryKey]='true' textAlign='Right' width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' width=180></e-column>
                        <e-column field='priority' headerText='Priority' textAlign='Right' width=90></e-column>
                        <e-column field='startDate' headerText='Start Date' textAlign='Right' format='yMd' type='date' editType='datepickeredit' width=90></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' width=80></e-column>
                    </e-columns>
                </ejs-treegrid>`
})
export class AppComponent implements OnInit {

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

Customize edit dialog

The edit dialog in the TreeGrid component allows you to customize its appearance and behavior based on the type of action being performed, such as editing or adding a record. You can modify properties like header text, close icon, and height to tailor the edit dialog to your specific requirements. Additionally, you can override default localization strings to provide custom text for buttons or other elements within the dialog.

To customize the edit dialog, you need to handle the actionComplete event of the TreeGrid component and perform the necessary modifications based on the requestType parameter. The requestType parameter identifies the type of action being performed, such as beginEdit for editing a record or add for adding a new record.

You can refer the tree grid Default text list for more localization.

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

import { NgModule,ViewChild } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { PageService, SortService, FilterService,EditService,ToolbarService } from '@syncfusion/ej2-angular-treegrid'

import { L10n } from '@syncfusion/ej2-base';
import { Component, OnInit } from '@angular/core';
import { sampleData } from './datasource';
import { EditSettingsModel, ToolbarItems } from '@syncfusion/ej2-angular-treegrid';

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

@Component({
    imports: [TreeGridModule],

    providers: [PageService,
                SortService,
                FilterService,
                EditService,
                ToolbarService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid [dataSource]='data' [toolbar]='toolbarOptions' [treeColumnIndex]='1' height='270' [editSettings]='editSettings' (actionComplete)="actionComplete($event)" childMapping='subtasks' >
                    <e-columns>
                        <e-column field='taskID' headerText='Task ID' [isPrimaryKey]='true' textAlign='Right' width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' width=180></e-column>
                        <e-column field='priority' headerText='Priority' textAlign='Right' width=90></e-column>
                        <e-column field='startDate' headerText='Start Date' textAlign='Right' format='yMd' type='date' editType='datepickeredit' width=90></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' width=80></e-column>
                    </e-columns>
                </ejs-treegrid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbarOptions?: ToolbarItems[];

    ngOnInit(): void {
        this.data = sampleData;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
        this.toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    }

    actionComplete(args: any) {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            const dialog = args.dialog;
            const taskName = 'taskName';
            dialog.showCloseIcon = false;
            dialog.height = 400;
            // change the header of the dialog
            dialog.header = args.requestType === 'beginEdit' ? 'Edit Record of ' + args.rowData['taskName'] : 'New Task Details';
        }
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

The Tree Grid add or edit dialog element has the max-height property, which is calculated based on the available window height. So, in the normal window (1920 x 1080), it is possible to set the dialog’s height up to 658px.

Show or hide columns in dialog editing

The show or hide columns of dialog editing feature in the tree grid allows you to dynamically control the visibility of columns while editing in the dialog edit mode. This feature is useful when you want to display specific columns based on the type of action being performed, such as editing an existing record or adding a new record. To achieve this, you can utilize the actionBegin event of the tree grid.

The actionBegin event is triggered whenever an action is initiated in the tree grid, such as editing, adding, or deleting a record. Within the event handler, you can check the requestType parameter to determine the type of action being performed. If the requestType is beginEdit or add, you can modify the visibility of columns using the column.visible property. This property is used to determine whether a column should be displayed or hidden. Then, when the requestType is save, you can reset the column visibility to its initial state using the column.visible property.

In the below example, we have rendered the tree grid columns priority as hidden column and duration as visible column. In the edit mode, we have changed the priority column to a visible state and duration column to a hidden state.

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { PageService, SortService, FilterService,EditService,ToolbarService } from '@syncfusion/ej2-angular-treegrid'

import { Component, OnInit, ViewChild } from '@angular/core';
import { sampleData } from './datasource';
import { EditSettingsModel, ToolbarItems, TreeGridComponent, Column } from '@syncfusion/ej2-angular-treegrid';
import { SaveEventArgs, EditEventArgs } from '@syncfusion/ej2-angular-grids';

@Component({
    imports: [TreeGridModule ],

    providers: [PageService,
                SortService,
                FilterService,
                EditService,
                ToolbarService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid #treegrid [dataSource]='data' [toolbar]='toolbarOptions' [treeColumnIndex]='1' height='270' [editSettings]='editSettings' (actionComplete)="actionComplete($event)" (actionBegin)="actionBegin($event)" childMapping='subtasks'>
                    <e-columns>
                        <e-column field='taskID' headerText='Task ID' [isPrimaryKey]='true' textAlign='Right' width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' width=180></e-column>
                        <e-column field='priority' headerText='Priority' [visible]='false' textAlign='Right' width=90></e-column>
                        <e-column field='startDate' headerText='Start Date' textAlign='Right' format='yMd' type='date' editType='datepickeredit' width=90></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' width=80></e-column>
                    </e-columns>
                </ejs-treegrid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbarOptions?: ToolbarItems[];
    @ViewChild('treegrid') treegrid?: TreeGridComponent;

    ngOnInit(): void {
        this.data = sampleData;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
        this.toolbarOptions = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
    }

    actionBegin(args: EditEventArgs) {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            for (const cols of (this.treegrid as TreeGridComponent).grid.columns) {
                if ((cols as Column).field === 'priority') {
                    (cols as Column).visible = true;
                } else if ((cols as Column).field === 'duration') {
                    (cols as Column).visible = false;
                }
            }
        }
    }

    actionComplete(args: SaveEventArgs) {
        if (args.requestType === 'save') {
            for (const cols of (this.treegrid as TreeGridComponent).grid.columns) {
                if ((cols as Column).field === 'priority') {
                    (cols as Column).visible = false;
                } else if ((cols as Column).field === 'duration') {
                    (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));

Use wizard like dialog editing

Wizard-like dialog editing enables the creation of intuitive step-by-step forms. This feature provides a structured approach to form completion or data entry by breaking down the process into manageable steps. This feature is particularly useful when you have complex forms that need to be broken down into smaller sections to guide you through the data entry process.

To achieve wizard-like dialog editing in the TreeGrid component, you can use the dialog template feature. This feature allows you to define your own custom editing template using the editSettings.mode property set to Dialog and editSettingsTemplate as template variable in NgTemplate to define the tree grid editors for each step of the wizard.

The following example demonstrate the wizard like editing in the tree grid with the unobtrusive validation.

import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common';
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { PageService, SortService, FilterService,EditService,ToolbarService } from '@syncfusion/ej2-angular-treegrid'
import { CheckBoxAllModule, ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns';
import { NumericTextBoxModule } from '@syncfusion/ej2-angular-inputs';
import { Component, OnInit, ViewChild } from '@angular/core';
import { sampleData } from './datasource';
import { EditSettingsModel, ToolbarItems, TreeGridComponent, Column } from '@syncfusion/ej2-angular-treegrid';
import { SaveEventArgs, EditEventArgs } from '@syncfusion/ej2-angular-grids';
import { FormGroup, NgForm } from '@angular/forms';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';

@Component({
    imports: [TreeGridModule, CheckBoxAllModule, ButtonModule, DropDownListAllModule, 
            NumericTextBoxModule, CommonModule, ReactiveFormsModule, FormsModule,],

    providers: [PageService,
                SortService,
                FilterService,
                EditService,
                ToolbarService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid #treegrid [dataSource]='data' [toolbar]='toolbarOptions' [treeColumnIndex]='1' height='270' [editSettings]='editSettings' (actionComplete)="actionComplete($event)" childMapping='subtasks'>
                    <e-columns>
                        <e-column field='taskID' headerText='Task ID' isPrimaryKey='true' textAlign='Right' width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' width=180></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' width=80></e-column>
                        <e-column field='priority' headerText='Priority' textAlign='Right' width=90></e-column>
                        <e-column field='approved' headerText='approved' textAlign='Right' width=80 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': taskID.invalid && (taskID.dirty || taskID.touched)}">
                                    <input [(ngModel)]="data.taskID" required id="taskID" name="taskID" type="text" [attr.disabled]="!data.isAdd ? '' : null" #taskID="ngModel">
                                    <span class="e-float-line"></span>
                                    <label class="e-float-text e-label-top" for="taskID"> Task ID</label>
                                </div>
                                <div id="taskIDError" *ngIf='taskID.invalid && (taskID.dirty || taskID.touched)'>
                                    <label class="e-error" id="taskID-info" style="display: block;">*Task 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': taskName.invalid && (taskName.dirty || taskName.touched)}">
                                    <input [(ngModel)]="data.taskName" required id="taskName" name="taskName" type="text" #taskName="ngModel">
                                    <span class="e-float-line"></span>
                                    <label class="e-float-text e-label-top" for="taskName">Task Name</label>
                                </div>
                                <div id="taskNameError" *ngIf='taskName.invalid && (taskName.dirty || taskName.touched)'>
                                    <label class="e-error" id="taskName-info" style="display: block;">*Task 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': duration.invalid && (duration.dirty || duration.touched)}">
                                    <div>
                                    <label class="e-float-text e-label-top" for="duration">Duration</label>
                                </div>
                                <!-- Use ejs-numerictextbox for numeric input -->
                                    <ejs-numerictextbox style="margin-top:10px" (ngModel)="data.duration"  id="duration" name="duration" #duration="ngModel" floatLabelType= 'Always'></ejs-numerictextbox>
                                </div>
                                
                            </div>
                        </div>
                        <div class="form-row">
                            <div class="form-group col-md-6">
                                <!-- Use ejs-dropdownlist for priority -->
                                <ejs-dropdownlist id="priority" name="priority" (ngModel)="data.priority" [dataSource]="priorityDistinctData" [fields]="{ text: 'priority', value: 'priority' }" 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-checkbox #approved name="approved" id="approved" label="approved" [checked]="data.approved"></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]="(taskName.invalid || taskID.invalid)">next</button>
                                </div>
                    </div>
                </div>
            </ng-template>
        </ejs-treegrid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbarOptions?: ToolbarItems[];
    public priorityDistinctData = ['Low', 'High', "Critical", 'Normal'];
    @ViewChild('treegrid') treegrid?: TreeGridComponent;
    public currentTab = 0;
    @ViewChild('orderForm') orderForm?: NgForm;

    ngOnInit(): void {
        this.data = sampleData;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
        this.toolbarOptions = ['Add', 'Edit', 'Delete'];
    }

    actionComplete(args: any) {
        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('taskName') as HTMLInputElement).focus();
          }
          this.currentTab = 0;
        }
      }
      saveBtn() {
        if (this.orderForm?.valid ) {
          (this.treegrid as TreeGridComponent).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();
        }
      }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

The Customize add/edit dialog footer feature in the tree grid allows you to modify the footer section of the dialog that appears when editing the currently selected row or adding a new row. By default, the dialog displays two buttons in the footer section: Save and Cancel, which allow you to save or discard the changes made in the dialog. This feature is particularly helpful when you want to add custom buttons to the dialog’s footer, implement specific actions, or customize the appearance of the buttons, such as changing their color or size in the dialog’s footer. This can be achieved using the actionComplete event of the TreeGrid component.

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

import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common';
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { PageService, SortService, FilterService,EditService,ToolbarService } from '@syncfusion/ej2-angular-treegrid'
import { Component, OnInit, ViewChild } from '@angular/core';
import { sampleData } from './datasource';
import { EditSettingsModel, ToolbarItems, TreeGridComponent, Column } from '@syncfusion/ej2-angular-treegrid';
import { SaveEventArgs, EditEventArgs } from '@syncfusion/ej2-angular-grids';


@Component({
    imports: [TreeGridModule],

    providers: [PageService,
                SortService,
                FilterService,
                EditService,
                ToolbarService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid #treegrid [dataSource]='data' [toolbar]='toolbarOptions' [treeColumnIndex]='1' height='270' [editSettings]='editSettings' (actionComplete)="actionComplete($event)" childMapping='subtasks'>
                    <e-columns>
                        <e-column field='taskID' headerText='Task ID' isPrimaryKey='true' textAlign='Right' width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' width=180></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' width=80></e-column>
                        <e-column field='priority' headerText='Priority' textAlign='Right' width=90></e-column>
                        <e-column field='approved' headerText='approved' textAlign='Right' width=80 type="boolean" [displayAsCheckBox]="true"></e-column>
                    </e-columns>
                </ejs-treegrid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    public editSettings?: EditSettingsModel;
    public toolbarOptions?: ToolbarItems[];
    public priorityDistinctData = ['Low', 'High', "Critical", 'Normal'];
    @ViewChild('treegrid') treegrid?: TreeGridComponent;
    

    ngOnInit(): void {
        this.data = sampleData;
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog' };
        this.toolbarOptions = ['Add', 'Edit', 'Delete'];
    }
    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.treegrid as TreeGridComponent).grid.editModule.closeEdit();
                }
              },
            {
              buttonModel: { content: 'Submit', cssClass: 'e-success custom-button-style' },
              click: () => {
                (this.treegrid as TreeGridComponent).grid.editModule.endEdit();
              }
            }
          ];
        }
      }

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