Getting error while editing in Dialog mode with 'editSettingsTmeplate'

Getting the following error when I click on the save button of edit Dialog.

core.mjs:7643 ERROR TypeError: Cannot read properties of null (reading 'parentElement')

    at FormValidator.getErrorElement (ej2-inputs.es2015.js:7389:96)

    at FormValidator.validateRules (ej2-inputs.es2015.js:7257:18)

    at FormValidator.validate (ej2-inputs.es2015.js:6920:22)

    at Edit.editFormValidate (ej2-grids.es2015.js:39090:51)

    at DialogEdit.editFormValidate (ej2-grids.es2015.js:36905:41)

    at DialogEdit.endEdit (ej2-grids.es2015.js:36914:42)

    at DialogEdit.endEdit (ej2-grids.es2015.js:38734:15)

    at Edit.endEditing (ej2-grids.es2015.js:39122:25)

    at Edit.endEdit (ej2-grids.es2015.js:39035:14)

    at GridComponent.endEdit (ej2-grids.es2015.js:17139:29)


GridCode from where I am getting this error:

getErrorElement(name) {
        this.infoElement = select(this.errorElement + '.' + this.errorClass, this.inputElement.parentElement);
        if (!this.infoElement) {
            this.infoElement = select(this.errorElement + '.' + this.errorClass + '[for="' + name + '"]', this.element);
        }
        return this.infoElement;
    }

Saving is working fine.

$event from beginAction when I click on edit toolbar option:

Screenshot 2022-09-22 at 6.05.37 PM.png


TS code:

import {Component, OnInit, ViewChild} from '@angular/core';
import {IUser} from "../../models/interface/i-user";
import {UserService} from "../../services/user.service";
import {
DataSourceChangedEventArgs,
EditSettingsModel,
ExcelExportProperties,
FilterSettingsModel,
GridComponent, InfiniteScrollSettings,
PdfExportProperties,
QueryCellInfoEventArgs,
RowDataBoundEventArgs,
RowSelectEventArgs, RowSelectingEventArgs,
ToolbarItems
} from "@syncfusion/ej2-angular-grids";
import {ClickEventArgs} from "@syncfusion/ej2-angular-navigations";
import {ActionBeginEventArgs, ActionCompleteEventArgs, MultiSelectComponent} from "@syncfusion/ej2-angular-dropdowns";
import {IOrganization} from "../../models/interface/i-organization";
import {OrganizationService} from "../../services/organization.service";
import {IRole} from "../../models/interface/i-role";
import {RoleService} from "../../services/role.service";
import {User} from "../../models/class/user";
import {Organization} from "../../models/class/organization";
import {LoggerService} from "../../logging/logger.service";
import {ServerError} from "../../models/interface/ServerError";
import {lastValueFrom} from "rxjs";
import {
RecordClickEventArgs
} from "@syncfusion/ej2-grids/src/grid/base/interface";

@Component({
selector : 'app-users',
templateUrl: './users.component.html',
styleUrls : ['./users.component.scss']
})
export class UsersComponent implements OnInit {

@ViewChild('grid')
public grid!: GridComponent;

userForm = new User();
actionInProgress!: string;
rolesMap = new Map<number, IRole>();
users: IUser[] | undefined;
organizations: IOrganization[] | undefined;
roleData: IRole[] | undefined;
// usersData!: { result: IUser[] | undefined, count: number | undefined };
usersData!: IUser[] | undefined;
editSettings!: EditSettingsModel;
toolbar!: ToolbarItems[];
filterOption: FilterSettingsModel = {
ignoreAccent: true,
type : "Excel"
}
serverError!: string;
testServerError = "TestError";
rowOddEvenCounter = 1;
public fields: Object = {text: 'name', value: 'id'};
editParams = {params: {popupHeight: '300px', allowFiltering: true}};
selectedOrg!: number;
showPassword = false;
selectedRoles!: number[];
usernameValidationRules = {required: true, minLength: 3};
selectedRowIndex!: number | undefined;

constructor(private _userService: UserService,
private _orgService: OrganizationService,
private _roleService: RoleService,
private _loggingService: LoggerService) {
}

ngOnInit(): void {
this.editSettings = {allowEditing: true, allowAdding: true, allowDeleting:true, mode: "Dialog"};
this.toolbar = ["Add", "Edit", "ColumnChooser", "PdfExport", "ExcelExport", "CsvExport", "Search"];

this._userService.getUsers$.subscribe(response => {
if(response.error) {
this.errorHandling(response.error);
} else {

this.users = response.serverData?.dataList;
this.usersData = this.users;
this._loggingService.debug("Users received from server:" + this.users?.length);
}
});

this._orgService.getOrgs$.subscribe(response => {
if(response.error) {
this.errorHandling(response.error);
} else {
this.organizations = response.serverData?.dataList;
this._loggingService.debug("Organisations received from server:" + this.organizations?.length);
}
});

this._roleService.getRoles$.subscribe(response => {
if(response.error) {
this.errorHandling(response.error);
} else {
this.roleData = response.serverData?.dataList;
if(this.roleData !== undefined) {
this.prepareRoleMap(this.roleData);
}
this._loggingService.debug("Roles received from server:" + this.roleData?.length);
}
});
}

private prepareRoleMap(roleData: IRole[]) {
for (let role of roleData) {
this.rolesMap.set(role.id, role);
}
}

toolbarClick($event: ClickEventArgs) {
let pdfExportProperties: PdfExportProperties = {fileName: "Users.pdf"}
let excelExportProperties: ExcelExportProperties = {fileName: "Users.xlsx"}
let csvExportProperties: ExcelExportProperties = {fileName: "Users.csv"}
if($event.item.id === 'grid_649044720_0_pdfexport') { // Id can be change in future
this.grid.pdfExport(pdfExportProperties).then(r => {
this._loggingService.info("PDF Export Done")
});
} else if($event.item.id === 'grid_649044720_0_excelexport') { // Id can be change in future
this.grid.excelExport(excelExportProperties).then(r => {
this._loggingService.info("Excel Export Done")
});
} else if($event.item.id === 'grid_649044720_0_csvexport') { // Id can be change in future
this.grid.csvExport(csvExportProperties).then(r => {
this._loggingService.info("CSV Export Done")
});
}
}

customiseRow(args: RowDataBoundEventArgs) {
// Change the bg color of row alternatively
if(this.rowOddEvenCounter % 2 == 0) {
this.rowOddEvenCounter -= 1;
args.row?.classList.add("bg-white");
} else {
this.rowOddEvenCounter += 1;
args.row?.classList.add("bg-gray-50");
}
}

customiseCell(args: QueryCellInfoEventArgs) {
const user: IUser = <IUser>args.data;
if(args.column?.field == "expired" && user.expired) {
args.cell?.classList.add("bg-red-300");
}
if(args.column?.field == "locked" && user.locked) {
args.cell?.classList.add("bg-purple-300");
}
if(args.column?.field == "enabled" && user.enabled) {
args.cell?.classList.add("bg-green-300");
}
if(args.column?.field == "sysAdminRole" && user.sysAdminRole) {
args.cell?.classList.add("bg-blue-300");
}
if(args.column?.field == "orgAdminRole" && user.orgAdminRole) {
args.cell?.classList.add("bg-green-500");
}
if(args.column?.field == "siteAdminRole" && user.siteAdminRole) {
args.cell?.classList.add("bg-yellow-300");
}
if(args.column?.field == "ownerRole" && user.ownerRole) {
args.cell?.classList.add("bg-orange-300");
}
if(args.column?.field == "operatorRole" && user.operatorRole) {
args.cell?.classList.add("bg-violet-200");
}
if(args.column?.field == "workerRole" && user.workerRole) {
args.cell?.classList.add("bg-emerald-300");
}
}

async actionBegin($event: any) {
console.log($event);
if($event.requestType === "add") {
if(this.actionInProgress !== "add") {
this.userForm = new User();
this.selectedOrg = -1;
this.selectedRoles = [];
}
this._loggingService.debug("Adding User start")
this.actionInProgress = "add";

}
if($event.requestType === "beginEdit") {
this._loggingService.debug("Editing User start")
this.actionInProgress = "beginEdit";
this.userForm = $event.rowData;
this.selectedOrg = this.userForm.organization?.id;
this.selectedRoles = [];
if(this.userForm.roles !== undefined && this.userForm.roles.length > 0) {
for (let role of this.userForm.roles) {
this.selectedRoles.push(role.id);
}
}
}
if($event.requestType === "save") {
console.log($event)
this.saveUser().then(responseUser =>{})}
}

private async saveUser(){
this._loggingService.debug("Saving User start")
this.prepareUserForSave();

let serverResponse$ = this._userService.addUser$(this.userForm)
let serverResponse = await lastValueFrom(serverResponse$);
let responseUser!: IUser;
if(serverResponse.error) {
this.errorHandling(serverResponse.error);
this.grid.selectRow(0);
this.grid.deleteRecord();
} else {
if(serverResponse.serverData?.data !== undefined) {
responseUser = serverResponse.serverData?.data;
// this.usersData?.push(responseUser)
// this.grid.refresh();
this.grid.updateRow(0, responseUser);
this.grid.selectRow(0);
}
}
}

private async updateUser(){
this._loggingService.debug("Updating User start")
this.prepareUserForSave();
let index = this.selectedRowIndex !== undefined ? this.selectedRowIndex : 0
let serverResponse$ = this._userService.updateUser$(this.userForm)
let serverResponse = await lastValueFrom(serverResponse$);
let responseUser!: IUser;
if(serverResponse.error) {
this.errorHandling(serverResponse.error);
this.grid.selectRow(index);
} else {
if(serverResponse.serverData?.data !== undefined) {
responseUser = serverResponse.serverData?.data;
this.grid.updateRow(index, responseUser);
this.grid.selectRow(index);
}
}
}

rowSelected($event: RecordClickEventArgs) {
this.selectedRowIndex = $event.rowIndex;
}

togglePassword() {
this.showPassword = !this.showPassword;
}


private prepareUserForSave() {
this.userForm.roles = this.getRoleObjFromSelectedRoles();
this.userForm.organization = this.getOrgObjFromSelectedOrg();
}

private getRoleObjFromSelectedRoles(): IRole[] {
let userRoles: IRole[] = [];
if(this.selectedRoles !== undefined) {
this.selectedRoles.forEach(roleId => {

let userRole = this.rolesMap.get(roleId);
if(userRole !== undefined) {
userRoles.push(userRole);
}
})
}
return userRoles;
}

private getOrgObjFromSelectedOrg(): IOrganization {
let org = new Organization();
org.id = this.selectedOrg;
return org;
}

private errorHandling(serverError: ServerError) {
this._loggingService.error(serverError);
if(serverError.status == 401) {
this.serverError = "Un-Authenticated, please logout and login again."
} else if(serverError.status == 403) {
this.serverError = "Un-Authorized, please contact your administrator."
} else {
this.serverError = serverError.displayMessage == null ? serverError.message : serverError.displayMessage;
}
}


}


HTML Code:

<app-nav-bar>
<div *ngIf = "serverError" error-div
class = "relative left-1/4 right-1/4 top-0 p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg dark:bg-red-200 dark:text-red-800 text-center w-1/2"
role = "alert">
<span class = "font-medium">Error !</span> {{serverError}}
</div>
<ejs-grid #grid main-div
[dataSource] = "usersData"
[allowSorting] = "true"
[allowFiltering] = "true"
[allowGrouping] = "true"
[editSettings] = "editSettings"
[toolbar] = "toolbar"
[enableStickyHeader] = "true"
[showColumnChooser] = "true"
[allowResizing] = "true"
[filterSettings] = "filterOption"
[allowPdfExport] = "true"
[allowExcelExport] = "true"
[allowMultiSorting] = "true"
(rowSelected)="rowSelected($event)"
(rowDataBound) = "customiseRow($event)"
(queryCellInfo) = "customiseCell($event)"
(toolbarClick) = "toolbarClick($event)"
(actionBegin) = "actionBegin($event)"
clipMode = "EllipsisWithTooltip"
width = "100%"
height="800">
<e-columns>
<e-column field = "id" [visible] = "false"
[showInColumnChooser] = "false"></e-column>

<e-column headerText = "Username" [isPrimaryKey] = "true" [allowResizing] = "true"
field = "username"
width = "150" [validationRules] = "usernameValidationRules"></e-column>

<e-column headerText = "Email" [allowResizing] = "true" field = "email"
width = "150"></e-column>

<e-column headerText = "Salutation" [allowResizing] = "true"
field = "salutation" width = "150"></e-column>

<e-column headerText = "First Name" [allowResizing] = "true" field = "firstName"
width = "150"></e-column>

<e-column headerText = "Middle Name" [allowResizing] = "true"
field = "middleName" width = "150"></e-column>

<e-column headerText = "Last Name" [allowResizing] = "true" field = "lastName"
width = "150"></e-column>

<e-column headerText = "Organization" [allowResizing] = "true"
editType = "dropdownedit" [edit] = "editParams"
field = "organization.name" width = "150">
</e-column>

<e-column headerText = "Contact Info" [allowResizing] = "true"
field = "contactInfo" width = "150"></e-column>

<e-column headerText = "IsSysAdmin" [allowResizing] = "true" field = "sysAdminRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "IsSiteAdmin" [allowResizing] = "true"
field = "siteAdminRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "IsOrgAdmin" [allowResizing] = "true" field = "orgAdminRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "IsOwner" [allowResizing] = "true" field = "ownerRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "IsOperator" [allowResizing] = "true" field = "operatorRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "IsWorker" [allowResizing] = "true" field = "workerRole"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "Locked" [allowResizing] = "true" field = "locked"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "Expired" [allowResizing] = "true" field = "expired"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>

<e-column headerText = "Logged-In" [allowResizing] = "true" field = "enabled"
displayAsCheckBox = "true" editType = "booleanedit"
width = "150"></e-column>
</e-columns>
<ng-template #editSettingsTemplate let-data>
<div ngForm #userEditForm = "ngForm"
class = "w-[500px] sm:w-[300px] ml-10 ml-auto mr-auto">
<input *ngIf = "actionInProgress ==='add'"
id="username"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
autocomplete = "username"
required
minlength = "3"
name = "username"
placeholder = "Username*"
[ngClass] = "!username.valid && username.touched?'box-shadow bg-red-200':''"
[(ngModel)] = "userForm.username"
#username = "ngModel" />
<input
id="salutation"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "salutation"
placeholder = "Salutation"
[(ngModel)] = "userForm.salutation"
#salutation = "ngModel" />
<input
id="firstName"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "firstname"
placeholder = "First Name*"
required
[ngClass] = "firstName.invalid && firstName.touched?'box-shadow bg-red-200':''"
[(ngModel)] = "userForm.firstName"
#firstName = "ngModel" />
<input
id="middleName"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "middleName"
placeholder = "Middle Name"
[(ngModel)] = "userForm.middleName"
#middleName = "ngModel" />
<input
id="lastName"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "lastname"
placeholder = "Last Name"
[(ngModel)] = "userForm.lastName"
#lastName = "ngModel" />

<input
id="email"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "email"
placeholder = "Email*"
required
[ngClass] = "email.invalid && email.touched?'box-shadow bg-red-200':''"
[(ngModel)] = "userForm.email"
#email = "ngModel" />
<input
id="contactInfo"
type = "text"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "contactInfo"
placeholder = "Contact Info"
[(ngModel)] = "userForm.contactInfo" />

<div class = "block border border-grey-light w-full p-3 rounded my-4"
[ngClass] = "roles.invalid && roles.touched?'box-shadow bg-red-200':''">
<ejs-multiselect id = "roles" [dataSource] = "roleData"
[(value)] = "selectedRoles" type = "Box"
[fields] = "fields"
name = "roles"
placeholder = "Select Roles*"
required
#roles = "ngModel"
[(ngModel)] = "selectedRoles">
</ejs-multiselect>
</div>
<div class = "block border border-grey-light w-full p-3 rounded my-4"
[ngClass] = "organization.invalid && organization.touched?'box-shadow bg-red-200':''">
<ejs-dropdownlist id = "organization.name" [dataSource] = "organizations"
[(value)] = "selectedOrg"
[fields] = "fields"
name = "organization"
required
placeholder = "Select Organisation*"
#organization = "ngModel"
[(ngModel)] = "selectedOrg">
</ejs-dropdownlist>
</div>

<div *ngIf = "actionInProgress === 'add'" class = "flex relative items-center">
<input
id="password"
[type] = "showPassword?'text':'password'"
autocomplete = "current-password"
required
minlength = "3"
class = "block border border-grey-light w-full p-3 rounded mb-4"
name = "password"
placeholder = "Password*"
[ngClass] = "userPassword.invalid && userPassword.touched?'box-shadow bg-red-200':''"
[(ngModel)] = "userForm.password"
#userPassword = "ngModel" />
<div class = "absolute right-2 top-3 w-[20px] h-[20px]">
<svg *ngIf = "!showPassword" (click) = "togglePassword()"
xmlns = "http://www.w3.org/2000/svg"
viewBox = "0 0 640 512">
<path d = "M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
</svg>
<svg *ngIf = "showPassword" (click) = "togglePassword()"
xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 576 512">
<path d = "M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
</svg>
</div>
</div>

<div class = "flex items-center mb-4 justify-between">
<div class = "flex items-center">
<input id = "locked" type = "checkbox"
class = "w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 focus:ring-2"
name = "locked"
[(ngModel)] = "userForm.locked">
<label for = "locked" class = "ml-2">Locked</label>
</div>
<div class = "flex items-center">
<input id = "expired" type = "checkbox" value = ""
class = "w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 focus:ring-2"
name = "expired"
[(ngModel)] = "userForm.expired">
<label for = "expired" class = "ml-2">Expired</label>
</div>
<div class = "flex items-center">
<input id = "enabled" type = "checkbox" value = ""
class = "w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 focus:ring-2"
name = "enabled"
[(ngModel)] = "userForm.enabled">
<label for = "enabled" class = "ml-2">Enabled</label>
</div>
</div>
</div>

</ng-template>
</ejs-grid>

</app-nav-bar>



4 Replies

PS Pavithra Subramaniyam Syncfusion Team September 23, 2022 10:58 AM UTC

Hi Surabhi Gangwar,


We have tried to reproduce the issue “script error while saving the edited data” at our end but it is working fine at our end. Please refer to the below sample for more information.


https://stackblitz.com/edit/angular-qx2jea?file=app.component.ts,app.component.html,app%2Fapp.module.ts,order.service.ts


Since there is more customization in your sample, we could not replicate the exact scenario and it seems that the issue occurs only in your sample. So could you please share an issue reproducible sample or hosted link for further validation that will be very helpful for us to validate the issue and provide a better solution as early as possible.


Regards,

Pavithra S



SG Surabhi Gangwar September 23, 2022 06:35 PM UTC

Hi Pavithra,


Sure, I will try to host somewhere where you can access that and will share the link with you.

Anyways, thanks for your reply.



SG Surabhi Gangwar September 25, 2022 07:23 AM UTC

I am able to solve this problem by adding following code:


actionComplete($event: any) {
if($event.requestType === 'add' || $event.requestType === 'beginEdit') {
$event.form.ej2_instances[0].rules = {};
}
}


PS Pavithra Subramaniyam Syncfusion Team September 26, 2022 04:41 AM UTC

Hi Surabhi Gangwar,


We are happy to hear that you have solved the issue.


Please get back to us if you need further assistance.


Regards,

Pavithra S


Loader.
Up arrow icon