17

How can I add a formcontrol(option) dynamically in formarray? I want to dynamically add questions to a formarray. Upon clicking a button, it should update the display. I'm using angular 7.

Component code

ngOnInit() {
  this.quizForm = this.fb.group({
    questions: this.fb.array([]),
    questions2: this.fb.array([]),
  });
}
    
//creating formcontrol
createItem(): FormGroup {
  return this.fb.group({
    ques: '',
  });
}
    
//pushing code
genField() {
  this.message = true;
  this.questions = this.quizForm.get('questions') as FormArray;
  this.questions.push(this.createItem());
}

HTML Template

I want to add form control option dynamically on button click and form control should be inside the formArrayName="questions".

<form [formGroup]="quizForm" class="adjust-form">
  <div formArrayName="questions" 
    *ngFor="let item of quizForm.get('questions').controls; let i = index;">
    <div class="col-md-10 col-sm-6 mt-3 mb-3" [formGroupName]="i">
      <label>{{i +1}} - {{question}} </label>
      <i class="flaticon-delete"    
          (click)="quizForm.get('questions').controls.splice(i,1) "
          style="font-size: 9px;padding-left: 80px;">
      </i>
      <div class="row">
        <input type="text" class="form-control col-10"
            [(ngModel)]="item.ques" formControlName="ques"
            placeholder="Enter your question" required>
        <button *ngIf="i == 0" (click)="genField()" type="button"
            class="btn btn-secondary btn-elevate btn-circle btn-icon ml-4">
            <i class="flaticon2-plus"></i>
        </button>
      </div>
      <div *ngIf="item.touched && item.invalid" class="kt-font-danger text-left">
        <p>required</p>
      </div>
    </div>
  </div>
</form>
1
  • Could you please add your html file snipper aswell? since i cant see anythign wrong in the ts file and are you getting any errors? Commented Nov 25, 2019 at 14:21

1 Answer 1

57

This example dynamically adds email fields to a reactive form. This would be used to enable users to add multiple email addresses (e.g. Home and Work).

This demo has the following dependencies: Angular 8, Angular Material, Bootstrap 4

End Result (Stackblitz Demo)

Angular Form Demo - dynamically add form controls

Step 1: Define the form model

constructor(private formBuilder: FormBuilder) { }

ngOnInit() {
  this.emailForm = this.formBuilder.group({
    emails: this.formBuilder.array([this.createEmailFormGroup()])
  });
}

Step 2: Define a method to dynamically construct new FormGroup

private createEmailFormGroup(): FormGroup {
  return new FormGroup({
    'emailAddress': new FormControl('', Validators.email),
    'emailLabel': new FormControl('')
  })
}

Step 3: Define a method to dynamically add new FormGroup to the FormArray

public addEmailFormGroup() {
  const emails = this.emailForm.get('emails') as FormArray
  emails.push(this.createEmailFormGroup())
}

Step 4 (Optional): Define method to delete FormGroup

public removeOrClearEmail(i: number) {
  const emails = this.emailForm.get('emails') as FormArray
  if (emails.length > 1) {
    emails.removeAt(i)
  } else {
    emails.reset()
  }
}

Step 5: Create the HTML form template

<form [formGroup]="emailForm">
  <div formArrayName="emails">
    <div class="row" *ngFor="let email of emailForm.get('emails').controls; let i = index"
        [formGroupName]="i">

Note that within the formArrayName element, the dynamic email FormGroups are named based on the array index.

Final Form

<mat-toolbar color="primary">
    Angular Form Demo - Dynamically add form controls
</mat-toolbar>

<form class="basic-container" [formGroup]="emailForm">
  <div formArrayName="emails">
    <div class="row" *ngFor="let email of emailForm.get('emails').controls; let i = index"
        [formGroupName]="i">

      <div class="col-1">
        <mat-icon class="mt-3">email</mat-icon>
      </div>

      <mat-form-field class="col-4">
        <input matInput formControlName="emailAddress" placeholder="Email" autocomplete="email">
        <mat-error *ngFor="let validation of validationMsgs.emailAddress">
          <div *ngIf="email.get('emailAddress').hasError(validation.type)">
            {{validation.message}}
          </div>
        </mat-error>
      </mat-form-field>

      <mat-form-field class="col-4">
        <mat-label>Label</mat-label>
        <mat-select formControlName="emailLabel">
          <mat-option *ngFor="let label of emailLabels" [value]="label">
            {{label}}
          </mat-option>
        </mat-select>
      </mat-form-field>

      <div class="col-3">
        <button class="float-left" mat-icon-button color="primary" aria-label="Remove/clear"
            (click)="removeOrClearEmail(i)" matTooltip="Remove">
          <mat-icon>highlight_off</mat-icon>
        </button>
        <button class="float-left" mat-icon-button color="primary" aria-label="Add"
            (click)="addEmailFormGroup()" matTooltip="Add">
          <mat-icon>add_circle_outline</mat-icon>
        </button>
      </div>
    </div>
  </div>
</form>

Final Component

import {Component} from '@angular/core';
import {FormBuilder, FormArray, FormControl, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'form-app',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  public emailForm: FormGroup;
  public emailLabels = ['Home', 'Work', 'Other'];
  public validationMsgs = {
    'emailAddress': [{ type: 'email', message: 'Enter a valid email' }]
  }

  constructor(private formBuilder: FormBuilder) { }

  ngOnInit() {
    this.emailForm = this.formBuilder.group({
      emails: this.formBuilder.array([this.createEmailFormGroup()])
    });
  }

  public addEmailFormGroup() {
    const emails = this.emailForm.get('emails') as FormArray
    emails.push(this.createEmailFormGroup())
  }

  public removeOrClearEmail(i: number) {
    const emails = this.emailForm.get('emails') as FormArray
    if (emails.length > 1) {
      emails.removeAt(i)
    } else {
      emails.reset()
    }
  }

  private createEmailFormGroup(): FormGroup {
    return new FormGroup({
      'emailAddress': new FormControl('', Validators.email),
      'emailLabel': new FormControl('')
    })
  }
}
Sign up to request clarification or add additional context in comments.

4 Comments

What if I want to make FormArray Push to something like unshift of javascript method ?
I created the POC using this solution, then I implemented similar functionality in my project. thanks @Christopher
If you're getting this error: "Cannot find control with name: '0'" — it means you're missing the formArrayName directive ;)
After push FormGroup to FormArray in step-3, I saw all of old items had been changed to new one!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.