The Angular FormArray example shows how to use the FormArray
. The FormArray allows us to add controls dynamically to the reactive forms. In this example, we will take a very simple task of dynamically adding/removing skills to an employee form.
Table of Contents
What is FormArray
The FormArray
is a way to manage the collection of Form Controls in Angular. The controls can be a FormGroup
, FormControl
, or another FormArray
.
We can group Form Controls in Angular forms in two ways. One is using the FormGroup
and the other one is FormArray
. The difference is how they implement it. In FormGroup
controls becomes a property of the FormGroup
. Each control is represented as key-value pair. While in FormArray
, the controls become part of an array
Because it is implemented as an Array, it makes it easier dynamically add controls.
FormArray Example
Let us build a simple app, which allows us to add the new skill of a person dynamically.
Import FormArray
To use FormArray, First, you need to import the FormArray
from the Angular Forms Module.
1 2 3 | import { FormGroup, FormControl,FormArray, FormBuilder } from '@angular/forms' |
Build a Form Model
Build a form model skillsForm
using the FormBuilder. Our Form has two fields. name
of the person and his skills
. Since the person can have more than one skill, we define skills
as FormArray
.
1 2 3 4 5 6 7 8 9 10 11 12 | skillsForm: FormGroup; constructor(private fb:FormBuilder) { this.skillsForm = this.fb.group({ name: '', skills: this.fb.array([]) , }); } |
Next, a getter
method skills
, which returns the skills
FormArray from the skillsForm
1 2 3 4 5 | get skills() : FormArray { return this.skillsForm.get("skills") as FormArray } |
The skill FormGroup
We need to capture two fields under each skill. Name of the skill
& years of exp
. Hence we create a FormGroup
with two fields. The method newSkill
creates a new FormGroup
and returns it. Note that we won’t be able to assign a name to Form Group.
1 2 3 4 5 6 7 8 | newSkill(): FormGroup { return this.fb.group({ skill: '', exp: '', }) } |
Dynamically adding skill
Now, we need to add a new skill to the skills
FormArray. Since it is an array
we can use the push
method to add the new skill using the the newSkill
method. Note that newSkill()
method returns a FormGroup
. The name of the FormGroup
is its Index in the FormArray
.
1 2 3 4 5 | addSkills() { this.skills.push(this.newSkill()); } |
Dynamically Removing Skill
Use the removeAt
method to remove the element from the skills
FromArray.
1 2 3 4 5 | removeSkill(i:number) { this.skills.removeAt(i); } |
Submit
1 2 3 4 5 | onSubmit() { console.log(this.skillsForm.value); } |
Template
Now, it is time to build the Template. Use the [formGroup]="skillsForm"
to bind the form to the skillsForm
model. The formControlName="name"
directive binds the name
input element to name
property of the skillsForm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <form [formGroup]="skillsForm" (ngSubmit)="onSubmit()"> <p> <label for="name">Name </label> <input type="text" id="name" name="name" formControlName="name"> </p> <p> <button type="submit">Submit</button> </p> </form> |
Binding FormArray to Template
We use the formArrayName
directive to bind the skills
form array to the div
element. Now the div
and anything inside the div
element is bound to the skills
form array.
1 2 3 4 5 | <div formArrayName="skills"> </div> |
Inside the div
use ngFor
to loop through each element of skills
FormArray. let i=index
will store the index
value of the array in template local variable i
. We will make use of it to remove the element from the skills
array.
1 2 3 4 5 6 7 | <div formArrayName="skills"> <div *ngFor="let skill of skills().controls; let i=index"> </div> </div> |
Each element under the skills
is a FormGroup
. We do not have a name to the FormGroup
. The Index of the element is automatically assigned as the name for the element.
Hence we use the [formGroupName]="i"
where i
is the index of the FormArray to bind the FormGroup
to the div
element.
1 2 3 4 5 6 7 8 9 | <div formArrayName="skills"> <div *ngFor="let skill of skills().controls; let i=index"> <div [formGroupName]="i"> </div> </div> </div> |
Finally, we add the controls using the formControlName
directive.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Skills: <div formArrayName="skills"> <div *ngFor="let skill of skills().controls; let i=index"> <div [formGroupName]="i"> {{i}} skill name : <input type="text" formControlName="skill"> exp: <input type="text" formControlName="exp"> <button (click)="removeSkill(i)">Remove</button> </div> </div> </div> |
Also, pass the index i
to removeSkill
1 2 3 | <button (click)="removeSkill(i)">Remove</button> |
Finally, call the addSkills
method to add new skills.
1 2 3 4 5 | <p> <button type="button" (click)="addSkills()">Add</button> </p> |
That’s it
Source Code
app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import { Component, ViewChild, ElementRef } from '@angular/core'; import { FormGroup, FormControl,FormArray, FormBuilder } from '@angular/forms' @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'FormArray Example in Angular Reactive forms'; skillsForm: FormGroup; constructor(private fb:FormBuilder) { this.skillsForm = this.fb.group({ name: '', skills: this.fb.array([]) , }); } get skills() : FormArray { return this.skillsForm.get("skills") as FormArray } newSkill(): FormGroup { return this.fb.group({ skill: '', exp: '', }) } addSkills() { this.skills.push(this.newSkill()); } removeSkill(i:number) { this.skills.removeAt(i); } onSubmit() { console.log(this.skillsForm.value); } } export class country { id: string; name: string; constructor(id: string, name: string) { this.id = id; this.name = name; } } |
app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <form [formGroup]="skillsForm" (ngSubmit)="onSubmit()"> <p> <label for="name">Name </label> <input type="text" id="name" name="name" formControlName="name"> </p> Skills: <div formArrayName="skills"> <div *ngFor="let skill of skills().controls; let i=index"> <div [formGroupName]="i"> {{i}} skill name : <input type="text" formControlName="skill"> exp: <input type="text" formControlName="exp"> <button (click)="removeSkill(i)">Remove</button> </div> </div> </div> <p> <button type="submit">Submit</button> </p> </form> <p> <button type="button" (click)="addSkills()">Add</button> </p> {{this.skillsForm.value | json}} |
References
Summary
In this tutorial, we learned how to create a simple FormArray Example app.
Complete List of Articles on Angular Forms
- Angular Forms Tutorial: Fundamental & Concepts
- Template Driven Forms in Angular
- Set Value in Template Driven forms in Angular
- Reactive Forms in Angular
- FormBuilder in Reactive Forms
- SetValue & PatchValue in Angular
- StatusChanges in Angular Forms
- ValueChanges in Angular Forms
- FormControl
- FormGroup
- FormArray Example
- Build Dynamic or Nested Forms using FormArray
- Validations in Reactive Forms in Angular
- Custom Validator in Reactive Forms
- Passing Parameter to Custom Validator in Reactive Forms
- Inject Service into Custom Validator
- Validation in Template Driven Forms
- Custom Validator in Template Driven Forms
if some user wants to add functionality that when he filled detail and click on add button than it display and form reset and he add in same box new data
So if I have a array of objects in the format {id: number, name: string, checked: boolean} and want to create a form of checkboxes, how to do it using the formArray? Is there way to insert a required validator to this form?
idk
You’re awsome!
need explanation for How to get the formarray controls to display error messages in HTML
Great!!
How can you start with already one ( or more) skill(s) in the form?
i tried to place the addSkills function inside the array but it doesn’t work
this.skillsForm = this.fb.group({
name: ”,
skills: this.fb.array([
this.addSkills()
]) ,
});
also put this formArray hardcode inside the formGrtoup doesn’t works
[[ {
“name”: “”,
“skills”: [
{
“skill”: “”,
“exp”: “”
}
]
} ]]
You should add form group While hardcoding .
this.skillForm = this.fb.group({
name : ” ,
skills : this.fb.array([this.fb.group({skill:’Angular’,exp : ‘2’ })])
})
error TS6234: This expression is not callable because it is a ‘get’ accessor. Did you mean to use it without ‘()’?
Type ‘FormArray’ has no call signatures.
11
This is giving Error. Any suggestion?
jsut remove ()
Terrific tutorial!
I was struggling to create a form to add a printer from the set of all known printers in the system but this tutorial gave me the way.
Thanks!
“Table of Content” should actually be “Table of Contents”
woow very well explained thanks
Incorrect: Below line gives error => ‘skills’ is not callable
let skill of skills().controls
Correct:
let skill of skills.controls
Explanation: skills is not a function, it is a getter.
Source: https://angular.io/guide/reactive-forms
Thanks you so much, just saved my life there.
thanks…. you really helped
Great article!
needs to be;
*ngFor=”let skill of skills().controls; let i=index”
needs to be
*ngFor=”let skill of skills().controls; let i=index”
Thanks
*ngFor=”let skill of skills().controls; let i=index” is correct
This is incorrect
The correct is:
incorrect: let skill of skillsControl.controls; let i=index
correct: let skill of skills.controls; let i=index
Thanks. Updated the code
thanks
in this you not explained how to change value of formarray controls
Yes. We are planning to write separate article on it.