So you have a form and would like to add form fields dynamically from a response to a user event? It’s easy to do with Reactive Forms and FormArray. FormArray is a bit like FormGroup and it’s used in a very similar way, the difference being that it’s used as an array that wraps around an arbitrary amount of FormControl, FormGroup or even other FormArray instances.
With FormArray you can therefore add new form fields or set of form fields declaratively.
Importing FormArray
First, on top of your other form imports, you’ll want to import FormArray from the Angular forms module:
import { FormBuilder, FormGroup, FormArray } from ‘@angular/forms’;
Initializing the Form
Now we’ll initialize our form using FormBuilder in the ngOnInit hook. For this example we’ll create an order form that allows the user to add new items dynamically:
orderForm: FormGroup;
items: FormArray;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.orderForm = this.formBuilder.group({
customerName: '',
email: '',
items: this.formBuilder.array([ this.createItem() ])
});
}
Our items instance is a form array instead of a form control, and we’re calling a createItem method to create a form group as the first item in our array. Here’s what our createItem method looks like:
createItem(): FormGroup {
return this.formBuilder.group({
name: '',
description: '',
price: ''
});
}
Adding to the FormArray Dynamically
Now the interesting part is that we can to treat our FormArray just like a regular array and push new items into it:
addItem(): void {
this.items = this.orderForm.get('items') as FormArray;
this.items.push(this.createItem());
}
Now it’s as simple as calling our addItem method in our template when the user clicks to add a new item.
FormArray in the Template
Finally, we use the formArrayName directive in the template to bind to our form array:
<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>
Chosen name: {{ orderForm.controls.items.controls[i].controls.name.value }}
</div>
Notice how our formGroupName directives now take an index instead of a name and we set it using the index that ngFor gives us.
You can also see a way to get to a form control’s value in the template by traversing our form.