Search code examples
angularnested-formsangular-reactive-formsformarraydynamicform

Nested dynamic array forms in Angular Reactive forms


I'm having a array form namely "address" and this will be a dynamic once the user clicks the "Add Address" button immediately one address form will add. I implemented this with an issue (Add/Remove address works fine). Now I need to add a dynamic contact numbers similar like address.

A address may contain one or more than one phone numbers, if the user clicks "Add Phone number" need to add a new phone number form inside the address form, the functionality will required in all the address forms. (i.e., array of array => array of address and each address contains array of contacts)

Working code is available in StackBlitz: https://stackblitz.com/edit/angular-maexn8

Sample Code:

import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
import { User } from '../../model/user';

@Injectable()
export class UpsertUserFormService {

  public userForm: FormGroup;

  constructor(private _fb: FormBuilder) {
    this.userForm = this._fb.group({
      relation: [],
      title: [],
      firstName: [],
      lastName: [],
      address: this._fb.array([this.addAddressGroup()])
    });
  }

  private addAddressGroup(): FormGroup {
    return this._fb.group({
      street: [],
      city: [],
      state: [],
      pincode: [],
      isPrimary: [],
      contacts: this._fb.array([this.contactsGroup()]) 
    });
  }

  showMessage(obj: any) {
    console.log('Console Item: ',obj)
  }

  private contactsGroup(): FormGroup {
    return this._fb.group({
      phoneNumber: ['9712345678', [Validators.maxLength(10)]], 
    });
  }

  addAddress(): void {
    this.addressArray.push(this.addAddressGroup());
    console.log(this.addressArray);
  }

  removeAddress(index: number): void {
    this.addressArray.removeAt(index);
  }

  get addressArray(): FormArray {
    return <FormArray>this.userForm.get('address');
  }

}

Kindly assist me how to implement dynamic nested array forms (Add/Remove forms).


Solution

  • You need to declare according to the formGroup or formArrays properly.

    You can use this as reference.

    Edit

    Forked your code: here. As mentioned above, keep the group/array/controls in order

    App.component.ts

    import { Component } from '@angular/core';
    import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {
      name = 'Angular';
      public userForm: FormGroup;
      constructor(private _fb: FormBuilder) {
        this.userForm = this._fb.group({
          firstName: [],
          lastName: [],
          address: this._fb.array([this.addAddressGroup()])
        });
      }
    
      private addAddressGroup(): FormGroup {
        return this._fb.group({
          street: [],
          city: [],
          state: [],
          contacts: this._fb.array([])
        });
      }
    
      addAddress(): void {
    
        this.addressArray.push(this.addAddressGroup());
    
        console.log(this.addressArray);
      }
    
      removeAddress(index: number): void {
        this.addressArray.removeAt(index);
      }
    
      get addressArray(): FormArray {
        return <FormArray>this.userForm.get('address');
      }
    
      addContact(index): void {
        
        (<FormArray>(<FormGroup>this.addressArray.controls[index]).controls.contacts).push(this.contactsGroup());
      }
    
      private contactsGroup(): FormGroup {
        return this._fb.group({
          contactPerson: [],
          phoneNumber: ['9712345678', [Validators.maxLength(10)]], 
        });
      }
    
      addPhoneNumber(index: number): void {
        this.addressArray[index].contacts.push(this.contactsGroup());
        console.log(this.addressArray[index].contacts);
      }
    }
    
    ## App.component.html ##
    <h3>Users Creation</h3>
    
    <form class="example-form" [formGroup]="userForm">
    	<div class="primary-container">
        <input matInput placeholder="First Name" value="" formControlName="firstName">
        <input matInput placeholder="Last Name" value="" formControlName="lastName">
    	</div>
    	<div class="address-container" *ngFor="let group of addressArray.controls; let i = index;" formArrayName="address">
    		<div [formGroupName]='i'>
    			<input matInput placeholder="Street" value="" formControlName="street"> 
          <input matInput placeholder="City" value="" formControlName="city">
          <input matInput placeholder="State" value="" formControlName="state">
        
        <div formArrayName='contacts'>
          <div *ngFor="let subgroup of group.controls.contacts.controls; let idx = index;" [formGroupName]="idx">
            <input matInput placeholder="Contact Person" value="" formControlName="contactPerson">
            <input matInput placeholder="Phone Number" value="" formControlName="phoneNumber">
          </div>
          <button mat-raised-button (click)="addContact(i)">Add more Contacts</button>
        </div>
        </div>
      </div>
      <div class="form-row org-desc-parent-margin">
        <button mat-raised-button (click)="addAddress()">Add more address</button>
      </div>
    </form>