While saving all my grid rows, I need to check whether there is a duplicate for a specific column field
(this.formGroups.get('items') as FormArray).controls.forEach((item) => {
console.log(item.value.attributeDisplayName);
});
I need to stop the form from submission by showing an alert or something similar when there is a duplicate value for attributeDisplayName. How can I check that in the current forEach loop. Thanks
I am using kendo angular components in my HTML markup and I am not using form tag. Below is an example for column declaration
<kendo-grid-column field="attributeDisplayName" title="CUSTOM FIELD LABEL" width="190">
<ng-template kendoGridEditTemplate let-column="column" let-formGroup="formGroup" >
<input ngDefaultControl class="k-textbox" [formControl]="formGroup.get(column.field)">
<div *ngIf="formGroup.get(column.field).errors && (formGroup.get(column.field).dirty || formGroup.get(column.field).touched)">
<span style="color:red" class="k-icon k-i-warning"></span>
<span style="color:red">CUSTOM FIELD LABEL is a required field</span>
</div>
</ng-template>
</kendo-grid-column>
Below is FormGroup declartion in .ts file
public formGroup: FormGroup;
public formGroups: FormGroup = new FormGroup({ items: new FormArray([]) });
public createFormGroup = (dataItem) =>
new FormGroup({
atrributeId: new FormControl(dataItem.atrributeId),
objectType: new FormControl(dataItem.objectType, Validators.required),
attributeDisplayName: new FormControl(dataItem.attributeDisplayName, Validators.required),
dataType: new FormControl(dataItem.dataType, Validators.required),
inputValues: new FormControl(dataItem.inputValues, [Validators.required, InputValuesValidator]),
isGridEligible: new FormControl(dataItem.isGridEligible, Validators.required),
isInvoiceEligible: new FormControl(dataItem.isInvoiceEligible, Validators.required),
});
An example function where I use this FormGroup
public editRows(grid) {
this.isEdited = true;
let currentRow = 0;
let rows: any = grid.data.data;
for (let i = 0; i < rows.length; i++) {
const formGroup = this.createFormGroup(rows[i]);
this.formGroup = formGroup;
(this.formGroups.get('items') as FormArray).push(formGroup);
grid.editRow(currentRow, formGroup, {skipFocus: true});
currentRow++;
}
}
Instead of checking it while submitting, just check it while filling form only and show the error immediately if user has filled duplicate value.
use this function to check duplicacy -
checkDuplicacy(event, field: FormControl) {
let length = this.formGroups.value.items.length;
let count = 0;
let controls = (<FormArray>this.formGroups.controls.items).controls;
for (let i = 0; i < length; i++) {
if (
this.formGroups.value.items[i].attributeDisplayName.toLowerCase() ==
event.target.value.toLowerCase()
) {
count++;
}
if (count > 1) {
field.markAsTouched();
field.setValidators((f) => <any>{ duplicateName: true });
field.updateValueAndValidity();
} else {
field.clearValidators();
field.setValidators([Validators.required]);
field.updateValueAndValidity();
}
}
}
And Updated HTML -
<kendo-grid-column field="attributeDisplayName" title="CUSTOM FIELD LABEL" width="190">
<ng-template kendoGridEditTemplate let-column="column" let-formGroup="formGroup" >
<input ngDefaultControl class="k-textbox" [formControl]="formGroup.get(column.field)" (blur)="checkDuplicacy($event, formGroup.get(column.field))">
<div *ngIf="formGroup.get(column.field).errors && (formGroup.get(column.field).dirty || formGroup.get(column.field).touched)">
<span style="color:red" class="k-icon k-i-warning"></span>
<span style="color:red">CUSTOM FIELD LABEL is a required field</span>
</div>
<span *ngIf="formGroup.get(column.field).errors?.duplicateName" style="color:red">
Duplicate field.</span>
</ng-template>
</kendo-grid-column>
Of course you can use a validator to check the duplicates. The validator is like
validateUniq(index) {
return (control: AbstractControl) => {
if (control.value) {
//search the "formArray"
const formArray = control.parent
? (control.parent.parent as FormArray)
: null;
if (formArray) {
//we create an array with the attributeDisplayNames
const attributes = formArray.value.map((x) => x.attributeDisplayName);
//only give error if there're duplicate before our
//control
return attributes.indexOf(control.value)>=0 &&
attributes.indexOf(control.value)<index
? { duplicateName: true }
: null;
}
}
};
}
You create the formGroup like -see how you pass the "index"
initFormField() {
const index = this.items ? this.items.length : 0;
return this.fb.group({
attributeDisplayName: [
'',
[Validators.required, this.validateUniq(index)],
],
});
}
The problem when we use a Validator over a control is that only is checked when the control changes, so we need make a function checkDuplicacy like @Shyam say -but in this case is more simple:
checkDuplicacy(index) {
this.items.controls.forEach((x,i)=>{
if (index!=i)
(x as FormGroup).get('attributeDisplayName').updateValueAndValidity()
})
}
The last piece to completate the jigsaw is a function to get the control
getAtributeDisplayNameAt(index: number) {
return this.items
? (this.items.at(index).get('attributeDisplayName') as FormControl)
: null;
}
And an html like
<form [formGroup]="formGroups" novalidate autocomplete="off">
<div formArrayName="items">
<div
*ngFor="let item of items.controls; let index = index"
[formGroupName]="index"
>
<div class="form-control">
<input
placeholder="Enter here"
formControlName="attributeDisplayName"
(input)="checkDuplicacy(index)"
/>
<span
*ngIf="getAtributeDisplayNameAt(index)?.errors?.duplicateName"
class="error"
>
Duplicate field.</span
>
</div>
</div>
</div>
<button (click)="addFormField()">Add More</button>
</form>
See how we call to the function checkDuplicacy in the event input -you can subscribe also to the valueChange of the control
The stackblitz