Template editing in EJ2 TypeScript Treegrid control

27 Apr 202319 minutes to read

Dialog template

The dialog template editing provides an option to customize the default behavior of dialog editing. Using the dialog template, you can render your own editors by defining the editSettings.mode as Dialog and template as SCRIPT element ID or HTML string which holds the template.

In some cases, you need to add the new field editors in the dialog which are not present in the column model. In that situation, the dialog template will help you to customize the default edit dialog.

In the following sample, treegrid enabled with dialog template editing.

import { TreeGrid, Edit, Toolbar, DialogEditEventArgs, SaveEventArgs } from '@syncfusion/ej2-treegrid';
import { sampleData } from './datasource.ts';
import { DataUtil } from '@syncfusion/ej2-data';
import { DatePicker } from '@syncfusion/ej2-calendars';
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { CheckBox } from '@syncfusion/ej2-buttons';
import { NumericTextBox } from '@syncfusion/ej2-inputs';

TreeGrid.Inject(Edit, Toolbar);

let treeGridObj: TreeGrid = new TreeGrid({
    dataSource: sampleData,
    childMapping: 'subtasks',
    toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Dialog',
    template: '#dialogtemplate' },
    treeColumnIndex: 1,
    columns: [
        { field: 'taskID', headerText: 'Task ID', isPrimaryKey: true, width: 90, textAlign: 'Right'},
        { field: 'taskName', headerText: 'Task Name', width: 180 },
        {
            field: 'startDate', headerText: 'Start Date', width: 90, textAlign: 'Right', type: 'date',format: 'yMd'
        },
        { field: 'progress', headerText: 'Progress', width: 80, textAlign: 'Right' }
    ],
    height: 270,
    actionBegin: (args: SaveEventArgs) => {
        if (args.requestType === 'save') {
            // cast string to integer value.
            args.data['progress'] = parseFloat(args.form.querySelector("#progress").value);
        }
    },
    actionComplete: (args: DialogEditEventArgs) => {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            // Add Validation Rules
            args.form.ej2_instances[0].addRules('progress', {max: 100});
            // EJ2-control Rendering
            let priorityData: {}[] = DataUtil.distinct(treeGridObj.grid.dataSource, 'priority',true);
            new DropDownList({value: args.rowData.priority, popupHeight: '200px', floatLabelType: 'Always',
                dataSource: priorityData, fields: {text: 'priority', value: 'priority'}, placeholder: 'Priority'}, args.form.elements.namedItem('priority') as HTMLInputElement);

            new DatePicker({value: args.rowData.startDate, floatLabelType: 'Always', placeholder: 'Start Date'}, args.form.elements.namedItem('startDate') as HTMLInputElement)

            new DatePicker({value: args.rowData.endDate, floatLabelType: 'Always', placeholder: 'End Date'}, args.form.elements.namedItem('endDate') as HTMLInputElement)

            new CheckBox({ label: 'Approved', checked: args.rowData.approved }, args.form.elements.namedItem('approved'));

            new NumericTextBox({value: args.rowData.progress, format: 'n', placeholder: 'Progress', floatLabelType: 'Always' }, args.form.elements.namedItem('progress') as HTMLInputElement );
             // Set initail Focus
            if (args.requestType === 'beginEdit') {
                (args.form.elements.namedItem('taskName')as HTMLInputElement).focus();
            }
        }
    }
});
treeGridObj.appendTo('#TreeGrid');
<!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 rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css">
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-grids/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-treegrid/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-dropdowns/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-lists/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-calendars/styles/material.css" rel="stylesheet" />
    
    
    
    <link href="https://cdn.syncfusion.com/ej2/25.1.35/ej2-splitbuttons/styles/material.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='TreeGrid'></div>        
    </div>
    <script id='dialogtemplate' type="text/x-template">
        <div>
            <div class="form-row" >
                <div class="form-group col-md-6">
                    <div class="e-float-input e-control-wrapper">
                        <input id="taskID" name="taskID" type="text" value=${if(isAdd)} '' ${else} ${taskID} ${/if}  ${if(isAdd)}'' ${else} disabled ${/if}/>
                        <span class="e-float-line"></span>
                        <label class="e-float-text e-label-top" for="OrderID">Task ID</label>
                    </div>
                </div>
                <div class="form-group col-md-6">
                    <div class="e-float-input e-control-wrapper">
                        <input id="taskName" name="taskName" type="text" value=${if(isAdd)} '' ${else} ${taskName} ${/if} />
                        <span class="e-float-line"></span>
                        <label class="e-float-text e-label-top" for="taskName">Task Name</label>
                    </div>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <div>
                        <input id="priority" name="priority" type="text" value=${if(isAdd)} '' ${else} ${priority} ${/if} />
                    </div>
                </div>
                <div class="form-group col-md-6">
                    <input id="progress" value=${if(isAdd)} '' ${else} ${progress} ${/if} />
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <div class="e-float-input e-control-wrapper">
                        <input type="text" name="startDate" id="startDate" value=${if(isAdd)} '' ${else} ${startDate} ${/if} />
                        <span class="e-float-line"></span>
                    </div>
                </div>
                <div class="form-group col-md-6">
                    <div class="e-float-input e-control-wrapper">
                        <input type="text" name="endDate" id="endDate" value=${if(isAdd)} '' ${else} ${endDate} ${/if} />
                        <span class="e-float-line"></span>
                    </div>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <input type="checkbox" name="approved" id="approved" ${if(isAdd)} '' ${else} checked ${/if} />
                </div>
            </div>
        </div>      
    </script>
</body>
</html>

The template form editors should have name attribute.

Template context

The Essential JS2 packages has a built-in template engine. Using the template engine, you can access the row information inside the HTML element and bind the attributes, values, or elements based on this row information.

The following properties will be available at the time of template execution.

Property Name Usage
isAdd A Boolean property; it defines whether the current row should be a new record or not.

In the following code example, the OrderID textbox has been disabled by using the isAdd property.

// The disabled attributes will be added based on the isAdd property.
<input id="taskID" name="taskID" type="text" value=${if(isAdd)} '' ${else} ${taskID} ${/if}  ${if(isAdd)}'' ${else} disabled ${/if}/>

The following code example illustrates rendering the taskID textbox, when a new record is added.


${if(isAdd)}
<div class="form-group col-md-6">
    <div class="e-float-input e-control-wrapper">
        <input id="taskID" name="taskID" type="text" value=${if(isAdd)} '' ${else} ${taskID} ${/if}  ${if(isAdd)}'' ${else} disabled ${/if}/>
        <span class="e-float-line"></span>
        <label class="e-float-text e-label-top" for="OrderID">Task ID</label>
    </div>
</div>
${/if}

The dialog template syntax supports the ES6 expression string literals, and you can refer to the Template Engine for more template syntax.

Render editors as components

You can convert the form editors to EJ2 controls in the actionComplete event based on the requestType as beginEdit or add.

The following code example illustrates rendering the drop-down list control in the actionComplete event.

    actionComplete: (args: DialogEditEventArgs) => {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            let priorityData: {}[] = DataUtil.distinct(treeGridObj.grid.dataSource, 'priority',true);
            new DropDownList({value: args.rowData.priority, popupHeight: '200px', floatLabelType: 'Always',
                dataSource: priorityData, fields: {text: 'priority', value: 'priority'}, placeholder: 'Priority'}, args.form.elements.namedItem('priority') as HTMLInputElement);
        }
    }

Get value from editor

You can read, format, and update the current editor value in the actionBegin event at the time of setting requestType to save.

In the following code example, the progress value has been formatted and updated.

    actionBegin: (args: SaveEventArgs) => {
        if (args.requestType === 'save') {
            // cast string to integer value.
            args.data['progress'] = parseFloat(args.form.querySelector("#progress").value);
        }
    }

Set focus to editor

By default, the first input element in the dialog will be focused while opening the dialog.
If the first input element is in disabled or hidden state, focus the valid input element in the actionComplete event based on requestType as beginEdit.

    actionComplete: (args: DialogEditEventArgs) => {
        // Set initail Focus
        if (args.requestType === 'beginEdit') {
            (args.form.elements.namedItem('taskName')as HTMLInputElement).focus();
        }
    }

Adding validation rules for custom editors

If you have used additional fields that are not present in the column model, then add the validation rules to the actionComplete event.

    actionComplete: (args: DialogEditEventArgs) => {
        if ((args.requestType === 'beginEdit' || args.requestType === 'add')) {
            // Add Validation Rules
            args.form.ej2_instances[0].addRules('progress', {max: 100});
        }
    }