Skip to main content

Angular FormArray Validation

FormArray tracks the value and validity state of an array of FormControlFormGroup or FormArray instances. To create a FormArraywe can pass an array of FormControl or FormGroup. A FormArray will be called validated only if its FormControl or FormGroup are validated. To provide a custom error messages on UI, we can fetch FormArray validation status in HTML template. 
On this page we will create a reactive form using FormBuilder and then we will validate and submit the form.

Technologies Used

Find the technologies being used in our example. 
1. Angular 8.0.3 
2. TypeScript 3.4.3 
3. Node.js 12.5.0 
4. Angular CLI 8.0.6

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 FormControlFormGroup 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 FormArraybecame 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 
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. FormArraywill 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 FormGroupcontrol() 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])
}); 
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 employeesform 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.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 employeesform 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: FormGroup;
	isValidFormSubmitted = null;
	allSkills: Observable<any[]>;
	constructor(
		private formBuilder: FormBuilder,
		private teamMngService: TeamManagementService) {
	}
	ngOnInit() {
		this.allSkills = this.teamMngService.getSkills();

		this.teamForm = this.formBuilder.group({
			teamName: ['', Validators.required],
			employees: this.formBuilder.array(
				[this.createEmpFormGroup()],
				[Validators.required])
		});
	}
	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 && isValidFormSubmitted != null && !isValidFormSubmitted" class="error">
        Add Employees.
      </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(idx)">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;
	skill = null;
} 
team.ts
import { Employee } from './employee';
export class Team {
	teamName: string;
	employees: Employee[];
} 
team-management.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
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 { } 








Comments

Popular posts from this blog

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...