I'm using editable with formarray. My model:
class Book {
id: number;
name: string;
active: boolean;
}
allBooks:
[
{id: 1, name: 'book1', active: true},
{id: 2, name: 'book2', active: true},
{id: 3, name: 'book3', active: true},
]
code snippet:
allBooks: Book[];
bookFg: FormGroup;
ngOnInit() {
this.bookFg = this.fb.group({
arrayForm: this.fb.array(allBooks.map(book => {
id: [book.id],
name: [book.name],
active: [book.active]
}))
});
}
I have to validate the book name, name is required and unique. The html snippet:
<div class="data-container" [formGroup]="bookFg">
<p-table id="resultTable" [columns]="cols" [value]="labelForm.get('arrayForm').controls" formArrayName="arrayForm" dataKey="value.id" scrollable="true" [resizableColumns]="true" scrollHeight="415px" selectionMode="single"
[selection]="selected" (onRowSelect)="onRowSelect($event.data)">
<ng-template pTemplate="header" let-columns>
...
...
...
</ng-template>
<ng-template pTemplate="body" let-rowData let-rowIndex="rowIndex">
<tr [pSelectableRow]="rowData" [formGroupName]="rowIndex">
<td>
<div class="text-center">
<input pInputText type="checkbox" formControlName="active">
</div>
</td>
<td pEditableColumn>
<p-cellEditor>
<ng-template pTemplate="input">
<input (focus)="onFocusEvent(rowIndex)" (blur)="onBlurEvent()" pInputText type="text" formControlName="name">
</ng-template>
<ng-template pTemplate="output">
{{rowData.get('name').value}}
</ng-template>
</p-cellEditor>
</td>
</tr>
</ng-template>
</p-table>
</div>
In this editable table, each row is formgroup. When after editing the name columns, this row will be saved. Question is how to validate? In my case, only save one row in one time. So should I validate all the formarray or just one formgroup in that formarray? and how?
just a customValidator over the array. Anyway there're a type error in your code, take a look to stackblitz
ngOnInit() {
this.bookFg = this.fb.group({
arrayForm: this.fb.array(
this.allBooks.map(book =>
this.fb.group({
id: [book.id],
name: [book.name],
active: [book.active]
})
)
)
});
}
myCustomValidator() {
return (formArray: FormArray) => {
let valid: boolean = true;
formArray.value.forEach((x, index) => {
if (formArray.value.findIndex(y => y.name == x.name) != index)
valid = false;
});
return valid ? null : { error: "Names must be unique" };
};
}
Update You can also create a validator only for the fields "name"
myCustomValidatorName(index) {
return (formControl: FormControl) => {
let valid: boolean = true;
if (index) {
const formArray =
formControl.parent && formControl.parent.parent
? (formControl.parent.parent as FormArray)
: null;
if (formArray) {
console.log(formControl.value);
formArray.value.forEach((x, i) => {
if (x.name == formControl.value && index>i) valid = false;
});
}
}
return valid ? null : { error: "Names must be inique" };
};
}
And you create the form like
ngOnInit() {
this.bookFg = this.fb.group({
arrayForm: this.fb.array(
this.allBooks.map((book, i) =>
this.fb.group({
id: new FormControl(book.id),
name: new FormControl(book.name, this.myCustomValidatorName(i)),
active: new FormControl(book.active)
})
),
this.myCustomValidator()
)
});
}
But the problem is that when change the name, not check the others name. so you must create a function
checkArray(index) {
(this.bookFg.get("arrayForm") as FormArray).controls.forEach((x, i) => {
if (i != index) x.get("name").updateValueAndValidity();
});
}
And call in the (input) of the edit -or subscribe to valuesChange-
<input formControlName="name" (input)="checkArray(i)">
Update how validate on submit or on blur
For validate on submit or blur, we need create the form with the constructor of formGroup and FormControl, not with FormBuilder adding {updateOn:'blur'}
or {updateOn:'submit'}
at the end of new FormGroup (*) in this case use (blur) in the input
this.bookFg = new FormGroup({
arrayForm: new FormArray(
this.allBooks.map((book, i) =>
this.fb.group({
id: new FormControl(book.id),
name: new FormControl(book.name,this.myCustomValidatorName(i)),
active: new FormControl(book.active)
})
),
this.myCustomValidator()
)
},{updateOn: 'blur'}); //<--this