angular-add-remove-formfields-to-formarray

Dynamically adding and removing form fields to FormArray | Angular Reactive Forms

There are three building blocks of Angular Reactive Form.

  1. FormControl
  2. FormGroup
  3. FormArray

In this post, We’ll see how FormArray can be used in reactive forms.

FormArray tracks the value and validity state of FormControl, FormGroup, FormArray instances.

It calculates its status by reducing the status values of its children. For example, if one of the controls in a FormArray is invalid, the entire array becomes invalid.

Example

This is a quick example of how to create a dynamic form in Angular. It’s a simple form for adding multiple addresses to the form. Think of like e-commerce website where you need to add multiple addresses. i.e Office Address, Home Address.

Here each address(formGroup we can say as well) contains 4 fields.
1.Street Address (required)
2.City (required)
3.State (required)
4.Zip (required)

Remove button for each address is used to remove it from the list.

Save is used to save all addresses. All the saved addresses will be shown in the list below the form.

App Module

In the module file, we’ll need to include ReactiveFormModule from “@angular/forms“, then only we’ll be able to use the building blocks of reactive forms, FormControl, FormArray, FormGroup.


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule ,ReactiveFormsModule} from '@angular/forms';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule ,ReactiveFormsModule],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

App Component

The component file defines the form fields and a couple of methods to add or remove the item from the FormArray.

Here in the component file, we’ve used addressForm FormGroup which is bound to the form element using FormGroup directive, creating a communication layer between the model and the form containing the inputs.

addressForm contains addressList property which is a FormArray. It is used to store the array of FormGroups for storing the list of addresses.

addAddress() method will add a new item to addressList formArray.

addAddress() method is calling createAddress() method, which is creating a FormGroup for address and then pushing it to the addressList. Here we’re pushing using getter function addresses. Getter function makes it easier to access the form controls.

save() method will assign the form value to the savedData variable which is we’re using to show the list below the form in the template.

removeAddressAt(i) method will remove the item from the addressList FormArray. Here we’re using removeAt() method of FormArray to remove the item from the FormArray.

FormArray contains the following methods:
1. at() : Get the AbstractControl at the given index in the array.
2. push(): Insert a new AbstractControl at the end of the array.
3. insert() : Insert a new AbstractControl at the given index in the array.
4. removeAt() : Remove the control at the given index in the array.
5. setControl() : Replace an existing control.
6. setValue() :Sets the value of the FormArray. It accepts an array that matches the structure of the control.
7. patchValue() : Patches the value of the FormArray. It accepts an array that matches the structure of the control, and does its best to match the values to the correct controls in the group.
8. reset() : Resets the FormArray and all descendants are marked pristine and untouched, and the value of all descendants to null or null maps.
9. getRawValue() : The aggregate value of the array, including any disabled controls.
10. clear() : Remove all controls in the FormArray.

You can read more about FormArray from official website: https://angular.io/api/forms/FormArray#description

Each address FormGroup contains the 4 fields
1.Street Address (required)
2.City (required)
3.State (required)
4.Zip (required)


import { Component } from "@angular/core";
import { FormGroup, FormArray, FormBuilder } from "@angular/forms";
@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  addressForm: FormGroup;
  savedData: any[];

  constructor(private fb: FormBuilder) {
    this.addressForm = this.fb.group({
      addressList: this.fb.array([])
    });
    this.addAddress();
  }

  ngOnInit() {}

  get addresses() {
    return this.addressForm.get("addressList") as FormArray;
  }

  addAddress() {
    this.addresses.push(this.createAddress());
  }

  removeAddressAt(i) {
    this.addresses.removeAt(i);
  }
  save() {
    this.savedData = this.addressForm.value;
  }

  createAddress() {
    return this.fb.group({
      streetAddress: [],
      city: [],
      state: [],
      zip: []
    });
  }
}

App Template

Template file is html content that’ll be displayed in the browser.

Here we’re using [formGroup] directive which will bind the addressForm to the template. also, form binds form submit event to the save() method of a component file using (ngSubmit)=”save()”

We’re using formArrayName directive to bind the addressList property to the template.

We’re binding looping nested form groups from addresses.controls using ngFor directive. Here addresses it a getter function that we’re using to access form control. we are binding nested form group using formGroup directive and binding the form group controls using formControlName directive.

Add New Address will add a new address to the list.

Remove button will remove the address from the list.

Save button will save the addresses to the savedData variable and will be display the list below the form.


<form [formGroup]="addressForm" (ngSubmit)="save()">
  <div formArrayName="addressList">
    <h3>Addresses</h3> <button (click)="addAddress()">Add New Address</button>
    <div *ngFor="let address of addresses.controls; let i=index">
      Address {{i+1}}
      <div class="form-container" [formGroup]="address">
        
        <div class="flex-50">
          <input formControlName="streetAddress" placeholder='Street'>
        </div>

        <div class="flex-50">
          <input formControlName="city" placeholder='City'>
        </div>

        <div class="flex-50">
          <input formControlName="state" placeholder='State' />
        </div>

        <div class="flex-30">
          <input formControlName="zip" placeholder='Zip' />
        </div>

        <button type="button" (click)="removeAddressAt(i)" class="flex-20">Remove</button>
      
      </div>
      <hr />
    </div>
  </div>
  <button type="submit" class="flex-20">Save</button>
</form>

<h2>Address List</h2>

<div *ngFor="let address of savedData?.addressList">
  <div>Street: {{address.streetAddress}}</div>
  <div>City: {{address.city}}</div>
  <div>State: {{address.state}}</div>
  <div>Zip: {{address.zip}}</div>
  <div>
  <hr />
</div>

You can see the working demo example here on stackblitz

Also Read:

If you like this post, please share it with your friends.

Leave a Comment