Angular FormArray Validation

By Arvind Rai, September 02, 2021
Angular FormArray tracks the value and validity state of an array of FormControl, FormGroup or FormArray instances. To create a FormArray, we can pass an array of FormControl or FormGroup. A FormArray is called validated only if its FormControl or FormGroup are validated. We can validate FormArray with synchronous and async validators.
On this page we will create a reactive form using FormBuilder and validate it.

Technologies Used

Find the technologies being used in our example.
1. Angular 12.1.0
2. Node.js 12.14.1
3. NPM 7.20.3

FormArray Validation

We can pass following arguments to instantiate FormArray.
FormArray(controls: AbstractControl[],
 validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], 
 asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[]) 
1. First argument is array of controls such as FormControl, FormGroup or FormArray.
2. Second argument is for sync validators. It is optional.
3. Third argument is for async validators. It is also optional.

Find the sample example.
We are using Validators.required at FormArray level. It means the size of FormArray must be greater than zero.
formArray = new FormArray([], [Validators.required]);
ngOnInit() {
	console.log(this.formArray.status);
	this.formArray.push(new FormControl());
	console.log(this.formArray.status);
} 
Output
INVALID
VALID 
Look into output, when there was no element in FormArray then validity status was invalid and when we pushed a control then FormArray became valid.

FormArray Validation using FormControl

Suppose FormArray is the array of FormControl. Then FormArray will not be considered validated if any of the FormControl is invalid. Find the code snippet.
formArray = new FormArray([
	new FormControl('', [Validators.required]),
	new FormControl('', [Validators.required, Validators.minLength(3)])
]);

ngOnInit() {
	console.log(this.formArray.status);
	this.formArray.setValue(["AA", "BBB"]);
	console.log(this.formArray.status);		
} 
Find the output.
INVALID
VALID 
The FormArray is containing two FormControl and they need to be validated and only when FormArray will be considered validated.

FormArray Validation using FormGroup

Suppose FormArray contains the array of FormGroup and each FormGroup contains FormControl with some validators. FormArray will be considered validated only if all the FormGroup are validated and FormGroup will be validated only if all the FormControl are validated. Find the code snippet.
formArray = new FormArray([
	new FormGroup({
		name: new FormControl('', [Validators.required]),
		age: new FormControl('', [Validators.min(18)])
	}),
	new FormGroup({
		name: new FormControl('', [Validators.required]),
		age: new FormControl('', [Validators.min(18)])
	})],		
);

ngOnInit() {
	console.log(this.formArray.controls[0].status);
	console.log(this.formArray.controls[1].status);
	console.log(this.formArray.status);

	this.formArray.setValue([{name: "Mahesh", age: 20}, {name: "Krishna", age: 25}]);

	console.log("---After setting value---");
	console.log(this.formArray.controls[0].status);
	console.log(this.formArray.controls[1].status);	
	console.log(this.formArray.status);		
} 
Find the output.
INVALID
INVALID
INVALID
---After setting value---
VALID
VALID
VALID 

FormArray Validation Example with FormBuilder

We will create a reactive form using FormBuilder which has methods such as group() to create FormGroup, control() to create FormControl and array() to create FormArray.
In our example we will create FormArray that will contain array of FormGroup and each FormGroup will contain some FormControl with validators. At run time we will push and remove controls from FormArray instance.
Find the code snippet to create team form.
this.teamForm = this.formBuilder.group({
	teamName: ['', Validators.required],
	employees: this.formBuilder.array(
		[this.createEmpFormGroup()],
		[Validators.required, Validators.maxLength(5)])
}); 
The createEmpFormGroup() method is creating a FormGroup as following. This will add an employee in team.
createEmpFormGroup() {
	return this.formBuilder.group({
		empName: ['', [Validators.required]],
		age: ['', [Validators.required, Validators.min(21)]],
		skill: ['', [Validators.required]],
	})
} 
To access validation state of FormArray and its elements, find the getter method for employees form array control.
get employees(): FormArray {
	return this.teamForm.get('employees') as FormArray;
} 
Now in HTML template we can access FormArray validation errors for custom error messages as following.
<label *ngIf="employees.errors?.required">
  Add Employees.
</label>
<label *ngIf="employees.errors?.maxlength" class="error">
  Maximum number of Employees can be 5.
</label>
<label *ngIf="employees.controls[i].get('age').errors?.required">
  Age required.
</label>
<label *ngIf="employees.controls[i].get('age').errors?.min">
  Minimum age is 21.
</label> 
In the above code i is the index of employees form array.
Find the print screen of output of our example. When we try to submit the form without validations, we will get following error messages.
Angular FormArray Validation
Now find the complete code.
team-management.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormArray, Validators, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';

import { TeamManagementService } from './team-management.service';
import { Team } from './team';
import { Employee } from './employee';

@Component({
	selector: 'app-team',
	templateUrl: './team-management.component.html'
})
export class TeamManagementComponent implements OnInit {
	teamForm = {} as FormGroup;
	isValidFormSubmitted: boolean | null = null;
	allSkills: Observable<any[]>;
	constructor(
		private formBuilder: FormBuilder,
		private teamMngService: TeamManagementService) {
		this.allSkills = this.teamMngService.getSkills();
	}
	ngOnInit() {
		this.teamForm = this.formBuilder.group({
			teamName: ['', Validators.required],
			employees: this.formBuilder.array(
				[this.createEmpFormGroup()],
				[Validators.required, Validators.maxLength(5)])
		});
	}
	createEmpFormGroup() {
		return this.formBuilder.group({
			empName: ['', [Validators.required]],
			age: ['', [Validators.required, Validators.min(21)]],
			skill: ['', [Validators.required]],
		})
	}
	get teamName() {
		return this.teamForm.get('teamName');
	}
	get employees(): FormArray {
		return this.teamForm.get('employees') as FormArray;
	}
	addEmployee() {
		let fg = this.createEmpFormGroup();
		this.employees.push(fg);
	}
	deleteEmployee(idx: number) {
		this.employees.removeAt(idx);
	}
	onFormSubmit() {
		this.isValidFormSubmitted = false;
		if (this.teamForm.invalid) {
			return;
		}
		this.isValidFormSubmitted = true;
		let team: Team = this.teamForm.value;
		this.teamMngService.saveTeam(team);
		this.teamForm.reset();
	}
	resetTeamForm() {
		this.teamForm.reset();
	}
} 
team-management.component.html
<h3>Create New Team</h3>
<div *ngIf="isValidFormSubmitted" class="submitted"> Form submitted successfully. </div>
<div class="team">
  <form [formGroup]="teamForm" (ngSubmit)="onFormSubmit()">
    <p>Team Name :
      <input formControlName="teamName">
      <br />
      <label *ngIf="teamName?.invalid && isValidFormSubmitted != null && !isValidFormSubmitted" class="error">
        Team name is required.
      </label>
    </p>
    <div class="all-emp">
      <b>Employees in Team:</b>
      <br>
      <label *ngIf="employees.errors?.required" class="error">
        Add Employees.
      </label>
      <label *ngIf="employees.errors?.maxlength" class="error">
        Maximum number of Employees can be 5.
      </label>
      <br>
      <div formArrayName="employees">
        <div *ngFor="let emp of employees.controls; let i = index" [formGroupName]="i" class="employee">
          <p>
            <b>Employee : {{i + 1}}</b>
          </p>
          <p>Name :
            <input formControlName="empName">
            <br />
            <label
              *ngIf="employees.controls[i].get('empName')?.errors?.required && isValidFormSubmitted != null && !isValidFormSubmitted"
              class="error">
              Employee name required.
            </label>
          </p>
          <p>Age :
            <input formControlName="age">
            <br />
            <label
              *ngIf="employees.controls[i].get('age')?.errors?.required && isValidFormSubmitted != null && !isValidFormSubmitted"
              class="error">
              Age required.
            </label>
            <label
              *ngIf="employees.controls[i].get('age')?.errors?.min && isValidFormSubmitted != null && !isValidFormSubmitted"
              class="error">
              Minimum age is 21.
            </label>
          </p>
          <p>Skill :
            <select formControlName="skill">
              <option *ngFor="let skill of allSkills | async" [ngValue]="skill.name">
                {{ skill.displayName }}
              </option>
            </select>
            <br />
            <label
              *ngIf="employees.controls[i].get('skill')?.errors?.required && isValidFormSubmitted != null && !isValidFormSubmitted"
              class="error">
              Select skill.
            </label>
          </p>
          <p>
            <button type="button" (click)="deleteEmployee(i)">Delete</button>
          </p>
        </div>
      </div>
      <button type="button" (click)="addEmployee()">Add More Employee</button>
    </div>
    <br />
    <button>SUBMIT</button>
    <button type="button" (click)="resetTeamForm()">RESET</button>
  </form>
</div> 
employee.ts
export class Employee {
	empName = null;
	age: number | null = null;
	skill = null;
} 
team.ts
import { Employee } from './employee';
export interface Team {
	teamName: string;
	employees: Employee[];
} 
team-management.service.ts
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { Team } from './team';

const ALL_SKILLS = [
	{ name: 'Java', displayName: 'Java' },
	{ name: 'Angular', displayName: 'Angular' },
	{ name: 'Dot Net', displayName: 'Dot Net' }
];

@Injectable({
	providedIn: 'root'
})
export class TeamManagementService {
	getSkills() {
		return of(ALL_SKILLS);
	}
	saveTeam(team: Team) {
		console.log('------------TEAM------------');
		console.log('Team Name: ' + team.teamName);
		console.log('----- Employee Details -----');
		for (let emp of team.employees) {
			console.log('Emp Name: ' + emp.empName);
			console.log('Emp age: ' + emp.age);
			console.log('Emp Skill: ' + emp.skill);
			console.log('-------------------');
		}
	}
} 
app.component.ts
import { Component } from '@angular/core';
 
@Component({
  selector: 'app-root',
  template: `
           <app-team></app-team>
          `
})
export class AppComponent {
} 
styles.css
.employee {
    border: 2px solid blue;
    width: 275px;
}
.error{
    color: red;
} 
.submitted{
    color: green;
    font-size: 20px;
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { TeamManagementComponent } from './team-management.component';

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

Run Application

To run the application, find the steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200

References

Angular FormArray
Angular FormBuilder

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us