BoldDesk®Customer service software offering ticketing, live chat, and omnichannel support, starting at $49/mo. for 10 agents. Try it for free.
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);
}
}