Tutorial

How To Dynamically Create Form Fields with FormArray in Angular

Updated on January 11, 2021
Default avatar

By Alligator.io

How To Dynamically Create Form Fields with FormArray in Angular

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 tutorial is out of date and no longer maintained.

Introduction

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.

Prerequisites

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.

Step 1 — Importing FormArray and Initializing the Form

First, ensure that you are importing ReactiveFormsModule in your application.

In app.module.ts, add an import for ReactiveFormsModule from the Angular forms module:

src/app/app.module.ts
// ...
import { ReactiveFormsModule } from '@angular/forms';

Also, add ReactiveFormsModule to the module’s array of imports:

src/app/app.module.ts
@NgModule({
  ...
  imports: [
    ...
    ReactiveFormsModule
  ]
  ...
})

In app.component.ts, add an import of FormBuilder, FormGroup, and FormArray from the Angular forms module:

src/app/app.component.ts
// ...
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';

Next, you will initialize the form using FormBuilder in the ngOnInit hook:

src/app/app.component.ts
// ...

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:

src/app/app.component.ts
// ...

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.

Step 2 — Adding to the FormArray Dynamically

We can treat our FormArray like a regular array and push new items into it. Add the addItem method to the AppComponent:

src/app/app.component.ts
// ...

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:

src/app/app.component.html
<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:

src/app/app.component.html
<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():

src/app/app.component.html
<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:

src/app/app.component.html
<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.

Conclusion

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.

Learn more about us


About the authors
Default avatar
Alligator.io

author



Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
6 Comments


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?

How do I duplicate all the 1st form group fields values to all the other from groups? please help me…

Thank you…!

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?

Awesome! Exactly what i was looking for. Thank U very much!

Thanks! How would you save the inputs to a datasource? Please create a tutorial for that feature.

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.

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel