Angular provides a powerful and flexible way to handle form validations using Reactive Forms. While Angular offers built-in validators, there are often cases where you need to implement custom validations, especially when dealing with complex data structures like arrays. In this blog, we will explore how to perform custom validations on arrays within Reactive Forms in Angular, with detailed examples.
If you want to learn one more feature of angular, you can refer here.
Prerequisites
Before diving into custom validation arrays, make sure you have a basic understanding of Angular and Reactive Forms. If not, consider checking the official Angular documentation for a comprehensive introduction.
Setting up the Angular Project
So, If you haven’t already set up an Angular project, you can do so by running the following commands:
ng new custom-validation-array-example
cd custom-validation-array-example
ng generate component my-form
Creating a Reactive Form
Inside the my-form
component, let’s create a reactive form that includes an array control. In this example, we’ll create a form to collect a list of email addresses.
// my-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
@Component({
selector: 'app-my-form',
templateUrl: './my-form.component.html',
styleUrls: ['./my-form.component.css']
})
export class MyFormComponent {
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
emailArray: this.fb.array([], [Validators.required, this.validateEmailArray])
});
}
// Custom validator for email array
validateEmailArray(control: FormArray): { [key: string]: any } | null {
const emails = control.value;
if (emails.some(email => !Validators.email(email))) {
return { invalidEmail: true };
}
return null;
}
get emailArray() {
return this.myForm.get('emailArray') as FormArray;
}
addEmail() {
this.emailArray.push(this.fb.control('', [Validators.required, Validators.email]));
}
removeEmail(index: number) {
this.emailArray.removeAt(index);
}
submitForm() {
if (this.myForm.valid) {
// Handle form submission
console.log(this.myForm.value);
} else {
// Form is invalid
}
}
}
n this code, we create a form group with a form array named emailArray
. We use a custom validator validateEmailArray
to ensure that all email addresses in the array are valid. The addEmail
and removeEmail
methods allow users to add and remove email inputs dynamically. Finally, the submitForm
method handles the form submission.
Building the Template
Now, let’s build the form template in my-form.component.html
:
<!-- my-form.component.html -->
<form [formGroup]="myForm" (ngSubmit)="submitForm()">
<div formArrayName="emailArray">
<div *ngFor="let email of emailArray.controls; let i = index">
<div class="form-group">
<label for="email{{ i }}">Email {{ i + 1 }}</label>
<input type="email" class="form-control" [formControlName]="i" id="email{{ i }}">
<button type="button" (click)="removeEmail(i)">Remove</button>
</div>
</div>
</div>
<button type="button" (click)="addEmail()">Add Email</button>
<button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>
This template displays the form array of email inputs and provides buttons to add and remove email addresses. The form submission button is disabled when the form is invalid.
Styling (Optional)
You can add some CSS to style your form to make it more user-friendly. Here’s an example of CSS to style the form:
/* my-form.component.css */
.form-group {
margin-bottom: 10px;
}
button {
margin-top: 10px;
}
Understanding Reactive Forms and Custom Validation
Reactive Forms in Angular are a powerful way to manage and validate forms. These forms are built using FormGroup
, FormControl
, and FormArray
classes from the @angular/forms
module. While Angular provides built-in validators like Validators.required
, Validators.email
, and more, there are times when you need to implement custom validation logic, especially when dealing with complex data structures like arrays.
What is a FormArray?
A FormArray
is a class in Angular that represents an array of form controls. It’s used to handle dynamic lists of data within a form. Each item in the array corresponds to a form control, which can have its own validation rules.
In the previous example, we created a form with a FormArray
named emailArray
to collect a list of email addresses. Here’s how we did it:
this.myForm = this.fb.group({
emailArray: this.fb.array([], [Validators.required, this.validateEmailArray])
});
this.fb.array([])
initializes an emptyFormArray
.[Validators.required, this.validateEmailArray]
specifies validators for the entireFormArray
.
Implementing Custom Array Validation
Therefore, to implement custom validation for a FormArray
, we define a custom validator function. In our example, we used validateEmailArray
:
// Custom validator for email array
validateEmailArray(control: FormArray): { [key: string]: any } | null {
const emails = control.value;
if (emails.some(email => !Validators.email(email))) {
return { invalidEmail: true };
}
return null;
}
In this validator function:
- We access the value of the
FormArray
usingcontrol.value
. - We use the
Array.prototype.some()
method to check if any of the email addresses are invalid based on theValidators.email
validator. - If any email is invalid, we return a validation error object with the key
invalidEmail
, indicating that theFormArray
is invalid. Otherwise, we returnnull
.
Displaying Validation Errors
Hence, to display validation errors in the template, you can use Angular’s formGroup
, formArrayName
, and formControlName
directives:
<form [formGroup]="myForm" (ngSubmit)="submitForm()">
<div formArrayName="emailArray">
<div *ngFor="let email of emailArray.controls; let i = index">
<div class="form-group">
<label for="email{{ i }}">Email {{ i + 1 }}</label>
<input type="email" class="form-control" [formControlName]="i" id="email{{ i }}">
<button type="button" (click)="removeEmail(i)">Remove</button>
<div *ngIf="email.invalid && email.touched">
<div *ngIf="email.hasError('required')">Email is required.</div>
<div *ngIf="email.hasError('email')">Invalid email format.</div>
</div>
</div>
</div>
</div>
<button type="button" (click)="addEmail()">Add Email</button>
<button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>
In this template:
- We use
formArrayName="emailArray"
to bind theFormArray
. - Inside the
*ngFor
loop, we use[formControlName]="i"
to bind each control. - We display validation errors using
*ngIf
directives and Angular’s error checking methods likeemail.hasError('required')
.
Dynamic Adding and Removing of Controls
Our form allows users to dynamically add and remove email input fields. Here’s how it’s done:
addEmail() {
this.emailArray.push(this.fb.control('', [Validators.required, Validators.email]));
}
removeEmail(index: number) {
this.emailArray.removeAt(index);
}
addEmail()
pushes a newFormControl
into theFormArray
when the “Add Email” button is clicked.removeEmail(index: number)
removes a control at the specified index when the “Remove” button is clicked.
Handling Form Submission
Finally, when the form is submitted, we check its validity before processing the data:
submitForm() {
if (this.myForm.valid) {
// Handle form submission
console.log(this.myForm.value);
} else {
// Form is invalid
}
}
We ensure that the form is valid before proceeding with form submission logic.
Conclusion
Angular Reactive Forms provide a robust mechanism for handling and validating forms, including forms with complex data structures like arrays. By creating custom validators and using the FormArray
class, you can implement custom array validations tailored to your application’s specific requirements. This tutorial has demonstrated how to create a form to collect a list of email addresses and implement custom validation for the email array. You can apply similar principles to other scenarios that involve dynamic arrays and custom validation logic in your Angular applications.
Finally, for more such posts, please follow our LinkedIn page- FrontEnd Competency.