This tutorial is out of date and no longer maintained.
In Angular 2+, Reactive Forms are available to manage the state of a form. FormArray
is used to track the value and validity state of form fields. You can use FormArray
in Reactive Forms to add form fields dynamically from a response to a user event.
FormArray
is used as an array that wraps around an arbitrary amount of FormControl
, FormGroup
, or even other FormArray
instances. With FormArray
you can add new form fields or a set of form fields declaratively.
In this article, we will go over an example application consisting of an order form for purchasing items. We will design this form to append a new form field for users to add items to their order.
To follow along with this article, you will need:
This post also assumes you are building from a fresh Angular project generated by @angular/cli
. You can refer to this post if you’re getting started with Angular CLI.
FormArray
and Initializing the FormFirst, ensure that you are importing ReactiveFormsModule
in your application.
In app.module.ts
, add an import
for ReactiveFormsModule
from the Angular forms
module:
// ...
import { ReactiveFormsModule } from '@angular/forms';
Also, add ReactiveFormsModule
to the module’s array of imports
:
@NgModule({
...
imports: [
...
ReactiveFormsModule
]
...
})
In app.component.ts
, add an import
of FormBuilder
, FormGroup
, and FormArray
from the Angular forms
module:
// ...
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
Next, you will initialize the form using FormBuilder
in the ngOnInit
hook:
// ...
export class AppComponent {
orderForm: FormGroup;
items: FormArray;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.orderForm = this.formBuilder.group({
customerName: '',
email: '',
items: this.formBuilder.array([ this.createItem() ])
});
}
}
In this example, orderForm
will consist of a customerName
, email
, and an array of items
.
Notice that the items
instance is a FormArray
instead of a FormControl
. We’re calling a createItem
method to create a FormGroup
as the first item in our array.
Next, we will add the createItem
method to the AppComponent
:
// ...
export class AppComponent {
// ...
createItem(): FormGroup {
return this.formBuilder.group({
name: '',
description: '',
price: ''
});
}
}
In our example, an item
will consist of a name
, description
, and a price
.
Now, we have an orderForm
and items
. We still have to implement a way to dynamically add new items.
FormArray
DynamicallyWe can treat our FormArray
like a regular array and push new items into it. Add the addItem
method to the AppComponent
:
// ...
export class AppComponent {
// ...
addItem(): void {
this.items = this.orderForm.get('items') as FormArray;
this.items.push(this.createItem());
}
}
Now we have addItem()
defined. We still have to call addItem
method in the template when the user clicks to add a new item.
Let’s use the formArrayName
directive in the template to bind to the FormArray
. In app.component.html
, replace the content with our new template:
<form [formGroup]="orderForm">
<div
formArrayName="items"
*ngFor="let item of orderForm.get('items')['controls']; let i = index;"
>
...
</div>
...
</form>
Next, let’s add our FormGroup
of FormControls
for an item
inside of the FormArray
:
<form [formGroup]="orderForm">
<div
formArrayName="items"
*ngFor="let item of orderForm.get('items')['controls']; let i = index;"
>
<div [formGroupName]="i">
<input formControlName="name" placeholder="Item name">
<input formControlName="description" placeholder="Item description">
<input formControlName="price" placeholder="Item price">
</div>
</div>
...
</form>
Notice how the formGroupName
directives now take an index instead of a name. We set it using the index that ngFor
gives us.
After the FormArray
, let’s add a button that when clicked calls addItem()
:
<form [formGroup]="orderForm">
<div
formArrayName="items"
*ngFor="let item of orderForm.get('items')['controls']; let i = index;"
>
<div [formGroupName]="i">
<input formControlName="name" placeholder="Item name">
<input formControlName="description" placeholder="Item description">
<input formControlName="price" placeholder="Item price">
</div>
</div>
<button type="button" (click)="addItem()">Add Item</button>
</form>
For debugging purposes, we can add some code to reveal FormControl
’s value in the template by traversing our form:
<form [formGroup]="orderForm">
<div
formArrayName="items"
*ngFor="let item of orderForm.get('items')['controls']; let i = index;"
>
<div [formGroupName]="i">
<input formControlName="name" placeholder="Item name">
<input formControlName="description" placeholder="Item description">
<input formControlName="price" placeholder="Item price">
</div>
Exposed item name: {{ orderForm.controls.items.controls[i].controls.name.value }}
</div>
<button type="button" (click)="addItem()">Add Item</button>
</form>
At this point, we have a form that starts with a single item. Once we enter a name
, description
, and price
for the first item, we can click the Add Item button and a new item is dynamically appended to the form.
You have completed an exploration of how Angular 2+ Reactive Forms and FormArray
can be used to add new form fields dynamically. This pattern is useful for scenarios where a user may need to enter data more than once.
If you’d like to learn more about Angular, check out our Angular topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
How do you add a validation, for example, a required validation?
I’m new to Angular. Does “last validated” date in your title mean you have updated this tutorial to use the latest version of Angular for 2021? Thanks.
Thanks! How would you save the inputs to a datasource? Please create a tutorial for that feature.
Awesome! Exactly what i was looking for. Thank U very much!
I did everything but got:
Cannot find control with path: 'users -> userList -> 0 -> userRole'
Did anyone get something similar, if so how do I solve this?How do I duplicate all the 1st form group fields values to all the other from groups? please help me…
Thank you…!