Suppose our form has date fields startDate and endDate, and we want to ensure that startDate must be before endDate.
To implement validations containing one or more sibling controls, we need to define the validation function at a higher level than the sibling controls.
ngOnInit() : void
{
this.formGroup = this.formBuilder.group(
{
startDate : new FormControl({
value : null,
disabled : false
}, [ Validators.required ]),
endDate : new FormControl({
value : null,
disabled : false
}, [ Validators.required ]),
},
// NOTE High-level validator
{ validator : DateValidator.dateRangeValidator });
}Then, we define a custom validator in a separate file, for example date.validator.ts.
// Angular modules
import { Injectable } from '@angular/core';
import { AbstractControl } from '@angular/forms';
import { ValidationErrors } from '@angular/forms';
@Injectable({
providedIn : 'root'
})
export class DateValidator
{
constructor() {}
public static dateRangeValidator(control : AbstractControl) : ValidationErrors | null
{
// NOTE Safety check
if (!control.get('startDate').value || !control.get('endDate').value)
return null;
// NOTE Field comparison
// const mStart = moment(control.get('startDate').value);
// const mEnd = moment(control.get('endDate').value);
// const isValid = mStart.isBefore(mEnd);
// NOTE Invalid
if (!isValid)
return { invalidDateRange : true };
// NOTE Valid
return null;
}
}If invalid, return an error object (with any arbitrary name), such as return { invalidEndDate: true }.
Make sure it always returns null for valid or irrelevant cases, and a non-null object in cases where an error should be triggered on the formGroup.
To display the error in our view, we use the invalidDateRange key returned by our validator.
<div *ngIf="formGroup.hasError('invalidDateRange')">
The start date is not before the end date
</div>