I have Angular form that is built with help of FormBuilder.
Form contains a FormArray which has as many fields as user wants. I've set validator for fields
this.fb.array([this.fb.control('', Validators.required)])
and for each new push
validator is the same.
The problem is that I don't know how to access a specific field's isValid
property since they are bound with FormControl
via [formControlName]="index"
.
I've tried to do it that way, but it doesn't seem to work
<div *ngIf="array.at(index).invalid" class="alert alert-danger p-2">
</div>
Where array
is a formArray.controls
passed from a parent.
There is a case https://stackblitz.com/edit/angular-7ppkoh
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);
}
}
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 :)