Since FormArray internally maintains an array of FormControls so it is easy to add or remove controls from it giving you capability to create a dynamic form using FormArray in Angular.
Dynamic Form using FormArray Angular example
In this FormArray example we’ll create a form where user has an option to add input text boxes or remove them dynamically.
Data Model (member.model.ts)
Member class defines the data model reflected in the form.
export class Member { name: string; mail: string; exercises: string[]; constructor(name: string, mail: string, exercises: string[]) { this.name = name; this.mail = mail; this.exercises = exercises; } }
CSS for Validation
In the form we show green bars at the left side of the controls that are required. That bar changes to red if value not entered for the required field or value is invalid and an error message is also displayed. Following CSS shows the green and red bars.
src/assets/forms.css
.ng-invalid.ng-untouched:not(form):not(div) { border-left: 5px solid #42A948; /* green */ } .ng-invalid.ng-touched:not(form):not(div) { border-left: 5px solid #a94442; /* red */ }
.ng-invalid.ng-untouched:not(form):not(div) means select all elements having both ng-invalid and ng-untouched set within its class attribute and it is not at form or div level. If you won’t add not(form) you will have a bar at the form level too encompassing all controls and not having at div level ensures that you don’t have a bar at the whole group level.
In the index.html file, update the <head> tag to include the new style sheet.
<link rel="stylesheet" href="assets/forms.css">
Component Class (app.component.ts)
import { Component, OnInit} from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms'; import { Member } from './member.model'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent implements OnInit { constructor(private fb: FormBuilder) {} member = new Member('', '', ['']); submitted = false; membershipForm!: FormGroup; ngOnInit() { this.membershipForm = this.fb.group({ memberName: ['', [Validators.required, Validators.minLength(5)]], email: ['', [Validators.required, Validators.email]], exercises: this.fb.array([this.createElement()]) }); } createElement(){ return this.fb.control('', Validators.required); } addExercise() { this.exercises.push(this.createElement()); } get exercises() { return this.membershipForm.get('exercises') as FormArray; } deleteControl(i: number){ if(this.exercises.length !== 1){ this.exercises.removeAt(i); } } onSubmit(){ this.submitted = true; this.member.name = this.membershipForm.value.memberName; this.member.mail = this.membershipForm.value.email; this.member.exercises = this.membershipForm.value.exercises; } }
Important points to note here are-
- In this ReactiveForm, FormBuilder is used to build the form so FormBuilder is imported.
- For using FormArray you will have to import FormArray from Angular Forms module.
import { FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms';
- exercises FormArray is created here using the FormBuilder.array() method that defines an array.
exercises: this.fb.array([this.createElement()])
- FormArray can be initialized with zero or more controls. Here it is initialized with a single FormControl.
- createElement() method is used to return a FormControl instance, Validators.required argument ensures that value has to be
entered for the control.
createElement(){ return this.fb.control('', Validators.required); }
- There is also a getter method exercises() returning the exercises FormArray. It is a convenience method to get easy access to the form array instance otherwise you will have to repeat the membershipForm.get('exercises') method each time.
- addExercise() method is used to push a new FromControl instance to the FormArray.
- deleteControl is used to remove control from the given index. Uses the removeAt() method of the FormArray which removes the
control at the given index in the array. We want to ensure that atleast one exercise is added that is why a check for length.
deleteControl(i: number){ if(this.exercises.length !== 1){ this.exercises.removeAt(i); } }
- When the form is submitted you create a Member instance using the entered values.
Template (app.component.html)
You can bind the exercises form array to the template using formArrayName directive.
<div class="container"> <h2>Gym Membership Form</h2> <form [formGroup]="membershipForm" (ngSubmit)="onSubmit()"> <div class="row mb-3"> <div class="col-sm-8"> <label for="name">Name</label> <input type="text" class="form-control" id="name" formControlName="memberName"> <div class="alert alert-danger" *ngIf="membershipForm.get('memberName')?.invalid && membershipForm.get('memberName')?.touched"> <div *ngIf="membershipForm.get('memberName')?.errors?.['required']"> Name is required. </div> <div *ngIf="membershipForm.get('memberName')?.errors?.['minlength']"> Name must be at least 5 characters long. </div> </div> </div> </div> <div class="row mb-3"> <div class="col-sm-8"> <label for="email">email</label> <input type="email" class="form-control" id="email" formControlName="email"> <div class="alert alert-danger" *ngIf="membershipForm.get('email')?.invalid && membershipForm.get('email')?.touched"> Please enter a valid email </div> </div> </div> <div class="row mb-3"> <div class="col-sm-8"> <div formArrayName="exercises"> <h4>Add favorite exercises</h4> <button class="btn btn-primary mb-2" type="button" (click)="addExercise()">Add Exercise</button> <table class="table"> <tr *ngFor="let exercise of exercises.controls; let i = index;"> <td><input type="text" class="form-control" [formControlName]="i"></td> <td><button class="btn btn-danger" type="button" (click)="deleteControl(i)">Delete</button></td> <td class="alert alert-danger" *ngIf="exercise.invalid && exercise.touched"> Please enter value </td> </tr> </table> </div> </div> </div> <button type="submit" [disabled]="membershipForm.invalid" class="btn btn-success">Submit</button> </form> </div> <hr> <div *ngIf="submitted"> <div class="row"> <div class="col-sm-8"> <p>Name: {{member.name}}</p> <p>email: {{member.mail}}</p> <p>Favorite Exercises: {{member.exercises}}</p> </div> </div> </div>
Important points to note here are-
- FormArray instance is bound to the template using formArrayName
<div formArrayName="exercises">
- Using
*ngFor directive you can iterate over each form control instance stored in the form array instance.
<tr class="form-group" *ngFor="let exercise of exercises.controls; let i = index;">
- Because form array elements are unnamed, you assign the index to the i variable and pass it to each control to bind it to the
formControlName input.
<input type="text" class="form-control" [formControlName]="i">
- Add exercise button gives you the option to dynamically add controls to your form.
- Delete button gives you the option to dynamically remove controls from your form.
- There is code for validation too. Please refer Angular Reactive Form Validation Example to know more about validation.
- On submitting the form the entered values are shown on the form.
<div *ngIf="submitted"> <div class="row"> <div class="col-sm-8"> <p>Name: {{member.name}}</p> <p>email: {{member.mail}}</p> <p>Favorite Exercises: {{member.exercises}}</p> </div> </div> </div>
That's all for this topic FormArray in Angular With Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Angular Tutorial Page
Related Topics
You may also like-
No comments:
Post a Comment