We can expand the use of validations and create custom functions that can even receive parameters to maximize reuse in our projects. To illustrate this, let’s create a custom validation to evaluate whether the number of repetitions or sets are multiples of two and three, respectively.
Let’s create a new file called custom-validation.ts and add the following function:
import { AbstractControl, ValidationErrors, ValidatorFn } from ‘@angular/forms’;
export function multipleValidator(multiple: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const isNotMultiple = control.value % multiple !== 0;
return isNotMultiple ?
{ isNotMultiple: { value: control.value } } : null;
};
}
For Angular to recognize the form validation function, it must return a new function with the signature described in the ValidatorFn interface. This signature defines that it will receive AbstractControl and must return an object of type ValidationErrors that allows the template to interpret the new type of validation.
Here, we get the input value using control.value, and if it is not a multiple of three, we will return the error object. Otherwise, we will return null, which will indicate to Angular that the value is correct.
To use this function, we are going to refactor our form component as follows:
.
.
.
ngOnInit() {
this.entryForm = this.formBuilder.group({
date: [”, Validators.required],
exercise: [”, Validators.required],
sets: [
”,
[Validators.required, Validators.min(0),
multipleValidator(2)
],
],
reps: [
”,
[Validators.required, Validators.min(0),
multipleValidator(3)
],
],
});
}
.
.
.
To use our custom function, we import it from the new file we created and use it in the validation array in the construction of the form object in the same way as standard Angular validations.
Finally, let’s change the form template to add the error message:
.
.
.
<div
*ngIf=”
entryForm.get(‘sets’)?.errors?.[‘isNotMultiple’] &&
entryForm.get(‘sets’)?.touched
“
class=”mt-1 text-red-500″
>
sets is required and must be multiple of 2.
</div>
.
.
.
<div
*ngIf=”
entryForm.get(‘reps’)?.errors?.[‘isNotMultiple’] &&
entryForm.get(‘reps’)?.touched
“
class=”mt-1 text-red-500″
>
Reps is required and must be multiple of 3.
</div>
.
.
.
We include the new div elements, but to specifically validate the error of multiples of the input, we use the error attribute and in it the new isNotMultiple attribute of our custom function.
We are using this parameter in square brackets because it is defined at runtime and Angular will warn at compile time that it does not exist.
Running our project, we can see the new validations:
Figure 6.4 – Gym Diary Form UI custom validations
In addition to validations, reactive forms from version 14 of Angular can be better typed to ensure higher productivity and security in the development of your project. We will go over this function in the next section.