Angular Syncfusion Custom Validation for ejs child grid column Not Mapping Properly After Save

I am using Syncfusion EJS Grid in Angular with custom validation for add/update operations . Before clicking the Save button, validation works correctly, highlighting errors as expected. However, after clicking Save, the validation does not map properly—either it resets or does not trigger correctly on certain fields

After clicking save button and edit any row it is showing older error msg.Even after deleteing cache it is displaying the older validation msg,even after removing the line from the code.

here is the code provided ,please let us know the changes required.

HTML


<ejs-grid #VariationQSORgrid id="VariationQSORgrid" [dataSource]='VariationQSORDatasource'

                                    [editSettings]='VariationQSOReditSettings' [toolbar]="VariationQSORtoolbar"

                                    height="220" [allowResizing]="true" [allowSorting]="true" gridLines='Both'

                                    [allowReordering]="true" showColumnMenu='true' [groupSettings]='groupOptions'

                                    allowFiltering='true' [enableStickyHeader]="true" [filterSettings]='filterSettings'

                                    [allowExcelExport]="true" [allowPdfExport]="true"

                                    [selectionSettings]="{ type: 'Multiple' }" [showColumnChooser]="true"

                                    (created)="onCreatedVariationQSOR()"

                                    (headerCellInfo)="headerCellInfoVariation($event)"

                                    (queryCellInfo)="queryCellInfoVariation($event)"

                                    (actionBegin)="onActionBeginVariationQSOR($event)"

                                    (actionComplete)="onActionCompleteVariationQSOR($event)"

                                    (columnMenuOpen)="columnMenuOpen($event)"

                                    (recordDoubleClick)="onDoubleClick($event)"

                                    (resizeStop)="columnResize(VariationQSORgrid,gridNames.VariationQSORgrid)"

                                    (columnMenuClick)="columnMenuClick(VariationQSORgrid,gridNames.VariationQSORgrid)">

                                    <e-columns>

                                        <e-column field='id' headerText='Id' width="50" [visible]="false"

                                            [showInColumnChooser]="false" textAlign="center"></e-column>

                                        <e-column field='commercialSuiteProjectId' headerText='CommercialSuiteProjectId'

                                            width="50" [visible]="false" [showInColumnChooser]="false"

                                            textAlign="center"></e-column>

                                        <e-column field='sorReference' headerTextAlign="center"

                                            [headerText]="l('Reference')" width='140' textAlign="left"

                                            [allowEditing]="isAddHoc" [validationRules]="SorReferenceQSORRule" >

                                            <ng-template #template let-record>

                                                <span tooltip="{{record.sorReference}}"

                                                    placement="left">{{record.sorReference}}</span>

                                            </ng-template>

                                        </e-column>


.ts



    public customQSORValidator: (args: { [key: string]: string }) => boolean = (args: { [key: string]: string }) => {


        const gridName = ((args.element as any).id).split((args.element as any).name)[0];

        let grid: GridComponent | undefined;


        if (gridName === 'VariationQSORgrid') {

            this.VariationQSORgrid.forEach((gridInstance: GridComponent) => {

                grid = gridInstance;

            });

        }

        const newValue = args.value;

        const isDuplicate = (grid?.dataSource as Object[]).some(item =>

            item['sorReference'] === newValue &&

            (!this.isEditMode|| item['sorReference'] !== this.editingOldSorRefVal)


        );


        if (isDuplicate) {


            return false;

        }

        return true;

    };


    SorReferenceQSORRule = { required: true, min: [this.customQSORValidator.bind(this),'Please Enter a Unique Reference Value'] };


  getAllChildGridItems(voId) {

        return new Promise(async (resolve, reject) => {

            const processChildItems = async (result) => {

                result = result && result.length > 0 ? result : [];

                this.VariationQSORDatasource = result.filter(f => f.childType == (this.isAddHoc ? "addHoc" : "qsor"));

                this.calculateTotalsVariationQSOR();

                await this.getAllVariationSummaryItems();

                if (this.isPageInitialized == false) {

                    this.StaticQSORArray = _.cloneDeep(this.VariationQSORDatasource);

                    this.isPageInitialized = true;

                }

                resolve(true);

            };

            var currentVariation = this.AllVariations.find(x => x.id == voId);

            if (currentVariation && currentVariation.id) {

                const currVariationVal = _.cloneDeep(currentVariation.value);

                this.VariationQSORDatasource = [];

                this.isAddHoc = currentVariation.isAddHocEnabled;

                if (!currentVariation.isStandaloneVoData && currentVariation.isAddHocEnabled) {

                    var _grossMargin = currVariationVal - currentVariation.cost;

                    this.VariationQSORDatasource.push({

                        id: 0,

                        sorReference: "",

                        description: "Un Categorized",

                        quantity: 1,

                        unitSell: currVariationVal || 0,

                        totalSell: currVariationVal || 0,

                        unitCost: currentVariation.cost || 0,

                        totalCost: currentVariation.cost || 0,

                        grossMargin: _grossMargin,

                        grossMarginPercentage: !currVariationVal ? 0 : (_grossMargin / currVariationVal) * 100,

                        costCodeName: "",

                        variationId: this.VariationForm.get('id').value || 0,

                        commercialSuiteProjectId: this.projectId,

                        isVariationsData: true,

                        isAddHocData: true,

                    });

                    this.calculateTotalsVariationQSOR();

                    await this.getAllVariationSummaryItems();

                    this.StaticQSORArray = _.cloneDeep(this.VariationQSORDatasource);


                    resolve(true);

                }

                else {

                    this._aFPVariationSummaryServiceProxy.getAllVariationsChildItems(this.projectId, this.isAddHoc, voId ?? 0).subscribe(processChildItems);

                }

            }

            else {

                this._aFPVariationSummaryServiceProxy.getAllVariationsChildItems(this.projectId, this.isAddHoc, 0).subscribe(processChildItems);

            }

        });

    }


 async saveDropDown(e) {

        await this.updateOpenEditRow();


        if (!this.ValidateObject(this.VariationForm.value)) {

            this.message.warn(this.l('ErrorMsg'));

            this.activeStepIndex = 0;

            return;

        }


        const voExists = this.AllVariations.some(x =>

            x.id !== this.VariationForm.get('id').value && x.voNumber?.toString()?.trim() === this.VariationForm.get('voNumber').value?.toString()?.trim()

        );


        if (voExists) {

            this.message.warn(this.l('Entered VO Number already exists for this Job'));

            return;

        }


        this.spinnerService.show();

        if (e.item.id === "save") {

            this.save(true).then(() => {

                this.spinnerService.hide();

                this.VariationQSORgrid.first?.refresh();

            });

        }

        else if (e.item.id === "saveAndClose") {

            this.save(true).then(() => {

                this.spinnerService.hide();

                this.close();

            });

        }

    }


  save(showSpinner = false) {

        return new Promise((resolve, reject) => {

            var _data: CreateOrEditAFPVariationSummaryDto = new CreateOrEditAFPVariationSummaryDto();

            _data = _.cloneDeep(this.VariationForm.value);

            _data.isAddHocEnabled = this.isAddHoc;

            _data.isStandaloneVoData = true;

            _data.commercialSuiteProjectId = this.projectId;

            _data.value = this.TotalPriceVS;

            _data.cost = this.TotalCostVS;

            _data.best = this.TotalPriceVS;

            _data.pm = this.AllVariations.find(f => f.id == this.VariationForm.get('id').value)?.pm || 0;

            const foundItem = this.CASdropdown.find(c => c.id == _data.customerAgreementStatusId);

            _data.ptp = foundItem ? !_data.value ? 0 : (foundItem.percentageOfBest * _data.value) / 100 : 0;


            _data.qsoRs = [];

            if (this.VariationQSORDatasource && this.VariationQSORDatasource.length > 0) {

                this.VariationQSORDatasource.forEach((element, index) => {

                    let dobj: QSORDto = new QSORDto();

                    Object.assign(dobj, element);

                    dobj.costCodeId = this.costCodes.find((costCode) => costCode.costCodeName === element.costCodeName)?.id;

                    _data.qsoRs[index] = dobj;

                });

            }

            this.spinnerService.show();

            this._aFPVariationSummaryServiceProxy.createOrEdit(_data).subscribe(async result => {

                this.spinnerService.hide();

                if (!_data.id && result && result.id > 0) {

                    _data.id = result.id;

                    this.VariationForm.controls['id'].setValue(result.id);

                }


                var idx = this.AllVariations.findIndex(x => x.id == result.id);

                if (idx == -1 && !this.AllVariations[0].id) this.AllVariations[0].id = result.id;

                idx = this.AllVariations.findIndex(x => x.id == result.id);

                if (idx != -1) Object.assign(this.AllVariations[idx], result);


                await this.getAllChildGridItems(result.id);

                this.StaticQSORArray = _.cloneDeep(this.VariationQSORDatasource);

                this.saveEmitter.emit(_data);

                if (showSpinner) this.message.info(this.l('SavedSuccessfully'));


                this.VariationQSORgrid.first?.refresh();

                resolve(true);

            },

                () => {

                    this.spinnerService.hide();

                    this.message.warn(this.l('FailedToSave'));

                }

            );

        });

    }


 onActionBeginVariationQSOR(args) {

        if (args.requestType === 'beginEdit') {

            if (!args.rowData.grossMarginPercentage) args.rowData.grossMarginPercentage = 0;


            this.isEditMode = true;

            this.editingOldSorRefVal = (args.rowData as any).sorReference;


        }

        else if (args.requestType === 'add') {

            this.isEditMode = false;

            this.editingOldSorRefVal = null;

        }



        if (args.requestType === "save") {

            args.data.totalSell = args.data.unitSell * args.data.quantity;

            args.data.totalCost = args.data.unitCost * args.data.quantity;

            args.data.grossMargin = args.data.totalSell - args.data.totalCost;

            args.data.grossMarginPercentage = (args.data.grossMargin / args.data.totalSell) * 100;

            args.data.commercialSuiteProjectId = this.projectId;


            const forbiddenValues = ['id', 'variationId', 'isVariationsData', 'isAddHocData', 'costCodeId'];

            for (const [key, value] of Object.entries(args.data)) {

                if (!forbiddenValues.includes(key) && (value == null || value === undefined || (typeof value === 'string' && value.trim().length === 0))) {

                    this.message.warn(this.l('Enter All Fields'));

                    return args.cancel = true;

                }

            }

        }

    }



 async onActionCompleteVariationQSOR(args) {

        var Grid: GridComponent;

        this.VariationQSORgrid.forEach((gridInstance: GridComponent) => {

            Grid = gridInstance;

        });

         console.log('request tyepe:', args.requestType);

        if (args.requestType === 'beginEdit') {

            args.form.querySelector('#' + Grid.element.id + this.setFocus.field).focus();

        }


        if (args.requestType === 'beginEdit' || args.requestType === 'add') {



            var _tempData: CreateOrEditQSORDto = new CreateOrEditQSORDto();

            for (var i = 0; i < args.form.elements.length; i++) {

                args.form.elements[i].oninput = () => {

                    var x = args.form.elements;

                    for (var j = 0; j < x.length; j++) {

                        const name = x[j].nextSibling?.getAttribute('name') || "";

                        const value = this.convertToNumber(x[j].value);

                        if (name) _tempData[name] = value;


                        switch (name) {

                            case "totalSell":

                                _tempData.totalSell = (_tempData.unitSell || 0) * (_tempData.quantity || 0);

                                x[j].value = _tempData.totalSell.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

                                break;

                            case "totalCost":

                                _tempData.totalCost = (_tempData.unitCost || 0) * (_tempData.quantity || 0);

                                x[j].value = _tempData.totalCost.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

                                break;

                            case "grossMargin":

                                _tempData.grossMargin = (_tempData.totalSell || 0) - (_tempData.totalCost || 0);

                                x[j].value = _tempData.grossMargin.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

                                break;

                            case "grossMarginPercentage":

                                _tempData.grossMarginPercentage = ((_tempData.grossMargin / _tempData.totalSell) * 100) || 0;

                                x[j].value = _tempData.grossMarginPercentage.toLocaleString('en-GB', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

                                break;

                            default:

                                break;

                        }

                    }

                }

            }


        }


        if (args.requestType === 'save') {



            if (this.VariationQSORDatasource && args.data) {

                const newReference = args.data.sorReference;

                const oldReference = args.previousData?.sorReference;

                console.log("old,new:",newReference,oldReference);

                const existingIndex = this.VariationQSORDatasource.findIndex(

                    (item) => item.sorReference === oldReference

                );


                if (existingIndex !== -1) {

                    //updating row

                    this.VariationQSORDatasource[existingIndex] = { ...args.data };

                } else {

                    //adding new row

                    const alreadyExists = this.VariationQSORDatasource.some(

                        (item) => item.sorReference === newReference

                    );

                    if (!alreadyExists) {

                        this.VariationQSORDatasource.push({ ...args.data });

                        this.VariationQSORDatasource.refresh();

                    }

                }


                this.VariationQSORDatasource = [...this.VariationQSORDatasource];


                await this.updateVariationSummaryDs();

                this.calculateTotalsVariationSummary();

                this.calculateTotalsVariationQSOR();

                this.VariationQSORgrid.first?.endEdit();

                this.VariationQSORgrid.first?.refresh();




            }

        }


        if (["columnstate", "reorder", "filtering", "sorting", "searching"].includes(args.requestType)) {

            var Grid: GridComponent;

            this.VariationQSORgrid.forEach((gridInstance: GridComponent) => {

                Grid = gridInstance;

            });


            Grid.refresh();

             this._utilsServiceComponent.createOrEditUserPersistance(Grid, this.gridNames.VariationQSORgrid, this.projectId);

        }

    }


Loader.
Up arrow icon