I am trying to implement a custom validator function to check either of the Phone numbers(Home Phone and Mobile) are entered or not. I want to show the error message on both the fields when they are both touched and not have the valid value, for some reason my code is not working as anticipated. Please help me with this piece. -Thanks! Here is the stackblitz link https://stackblitz.com/edit/angular-ve5ctu
createFormGroup() {
this.myForm = this.fb.group({
mobile : new FormControl('', [this.atLeastOnePhoneRequired]),
homePhone : new FormControl('', [this.atLeastOnePhoneRequired])
});
}
atLeastOnePhoneRequired(control : AbstractControl) : {[s:string ]: boolean} {
const group = control.parent;
if (group) {
if(group.controls['mobile'].value || group.controls['homePhone'].value) {
return;
}
}
let errorObj = {'error': false};
return errorObj;
}
Instead of marking the validator on each formControl, make a nested group for the phone numbers and apply the validator to that group. In this sample I'll just apply the validator on the whole form.
Also when applying validators we need to return null
when the field is valid.
Also, since you are using Angular material, we need to add a ErrorStateMatcher
to be able to show mat-errors
. mat-errors
show only when validators are set to form control, not a formgroup.
Your code should look like follows:
createFormGroup() {
this.myForm = this.fb.group({
mobile : new FormControl(''),
homePhone : new FormControl('')
// our custom validator
}, { validator: this.atLeastOnePhoneRequired});
}
atLeastOnePhoneRequired(group : FormGroup) : {[s:string ]: boolean} {
if (group) {
if(group.controls['mobile'].value || group.controls['homePhone'].value) {
return null;
}
}
return {'error': true};
}
The error state matcher:
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const controlTouched = !!(control && (control.dirty || control.touched));
const controlInvalid = !!(control && control.invalid);
const parentInvalid = !!(control && control.parent && control.parent.invalid && (control.parent.dirty || control.parent.touched));
return (controlTouched && (controlInvalid || parentInvalid));
}
}
which you mark in component with:
matcher = new MyErrorStateMatcher();
and then mark it to your template on both input fields. Also note how the *ngIf
looks for displaying validation messages:
<mat-form-field>
<input matInput placeholder="Mobile" formControlName="mobile" [errorStateMatcher]="matcher">
<mat-error *ngIf="myForm.hasError('error')">
"Enter either phone number"
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Home Phone" formControlName="homePhone" [errorStateMatcher]="matcher">
<mat-error *ngIf="myForm.hasError('error')">
"Enter either phone number"
</mat-error>
</mat-form-field>
StackBlitz
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With