Angular FormArray display validation errors
In the ngFor statement, you are defining the variable "feature" to use for each control of the form array.
*ngFor="let feature of features.controls; let index = index; let last = last"
You can use that variable to get the invalid status of that control.
feature.invalid
Here is the Stackblitz
Additionally
You don't need the required attribute in the HTML when you are using reactive forms.
So this
<input type="text" class="form-control px-2 col" [formControlName]="index" title="feature" required>
Can be
<input type="text" class="form-control px-2 col" [formControlName]="index" title="feature">
I don't really think this would be possible completely on the template. That's because to access the FormArray's control's state in your template, you'll have to access this.formGroup.get('features').controls[i].invalid
. But since get
returns an instance of type AbstractControl
, you won't have access to the controls
array on it. For that, you'll have to typecast whatever is returned from this.formGroup.get('features')
into a FormArray
. And I don't really think that would be possible on the template.
You'll have to create a method in your class that would return the validity of the control based on the index.
So if we continue to refer to your stackblitz eg, here's how:
<form [formGroup]="formGroup">
<div formArrayName="features">
<div
class="row no-gutters form-group"
*ngFor="let feature of features.controls; let index = index; let last = last">
<input
type="text"
class="form-control px-2 col"
[formControlName]="index"
title="feature"
required>
IS Invalid - {{ getValidity(index) }}
<div class="col-3 col-md-2 row no-gutters">
<button
class="col btn btn-outline-danger"
(click)="removeFeature(index)">
-
</button>
<button
class="col btn btn-success"
*ngIf="last"
(click)="addFeature()">
+
</button>
</div>
</div>
</div>
</form>
And in your class:
import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
constructor(
private fb: FormBuilder,
) {}
formGroup = this.fb.group({
features: this.fb.array([this.fb.control('', Validators.required)])
});
get features(): FormArray {
return this.formGroup.get('features') as FormArray;
}
addFeature(): void {
this.features.push(this.fb.control('', Validators.required));
}
getValidity(i) {
return (<FormArray>this.formGroup.get('features')).controls[i].invalid;
}
removeFeature(index): void {
this.features.removeAt(index);
console.log(this.features);
}
}
UPDATE
A few months back I realized that calling a method in one of the data-binding syntaxes(i.e. String Interpolation - {{ ... }}
, Property Binding - [propertyName]="methodName()"
, or Attribute Binding - [class.class-name]="methodName()"
OR [style.styleName]="methodName()"
) is extremely costly as far as performance is concerned.
So, you should do it using:
{{ formGroup.controls['features'].controls[index].invalid }}
Instead of:
{{ getValidity(index) }}
Here's an Updated Working Sample StackBlitz for your ref.
If you wanna know more about it, I highly recommend you to check this thread out:
Angular Performance: DOM Event causes unnecessary function calls
Hope this helps :)
I have this example in angular 8.
In your template when you do this.
<ng-container formArrayName="calibers">
<ng-container *ngFor="let item of qualityForm.get('calibers')['controls']; let index = index" [formGroupName]="index.toString()">
<ion-item>
<ion-label position="floating">{{ getCaliberName(item) }}</ion-label>
<ion-input formControlName="percentage" placeholder="Input Percentage" type="number" clearInput></ion-input>
<ng-container *ngIf="item.get('percentage').hasError('required')">
<span class="errorMsg">Input Percentage</span>
</ng-container>
<ng-container *ngIf="item.get('percentage').hasError('max')">
<span class="errorMsg">Percentage cannot be greater than 100</span>
</ng-container>
</ion-item>
</ng-container>
</ng-container>
That item object in the ngFor will give you access to the form control. all you need to do to get the array form errors is item.get('percentage').hasError('required')