Angular 2 custom validator to check when either one of the two fields is required
Asked Answered
R

2

8

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;
}
Risarise answered 5/12, 2017 at 19:57 Comment(1)
#31789181Sunless
Z
10

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

Zach answered 10/12, 2017 at 9:1 Comment(7)
how to make control Invalid here? Because, if we have other fields in this form, they all are going red when form is invalid (controlTouched && (controlInvalid || parentInvalid)) Eg: StackBlitz. @AJT_82 very thanks for the response.Risarise
Well don't set the errorStateMatcher on form controls. As I mentioned on top, it's needed when error is set on a formgroup. With form controls you don't need it :)Zach
I mean my comment: mat-errors show only when validators are set to form control, not a formgroup.Zach
(controlTouched && (controlInvalid || parentInvalid)) is making the current control red when there are other invalid controls, even though the current control is valid. May be I am not thinking in the right direction. Thanks for your help though.Risarise
I don't get what you mean? As said, if you remove the error state matcher from the form controls, it shouldn't do that? stackblitz.com/edit/angular-kcqyse-nuesey?file=app/…Zach
@AJT_82, {'error': true} is returned when the condition is not met. While it's not crucial as I have this working in my application, I'm trying to understand why this particular object needs to be returned when not valid. I couldn't find anything in the Angular documentation but might have missed it. ThanksGrist
@bmd, you are setting an error for your form with this. So for example in the template if you want to show error message, you can say *ngIf="myForm.hasError('error')" :) You can yourself choose what error you want to set, here error is quite a bad example, you should use something more explanatory ;)Zach
A
0

In my simple case I used [required] with a formula in both fileds

<form
    #clienteForm="ngForm"
    (ngSubmit)="onSubmit(clienteForm.value)">
    
    ...
    
    <input matInput name="partitaIva" placeholder="Partita IVA"  ngModel
           [required]="!clienteForm.value.codFiscale">
    <input matInput name="codFiscale" placeholder="Cod.Fiscale"  ngModel
           [required]="!clienteForm.value.partitaIva">  

    ...            
    
</form> 

the result is that either one of thw fields is required

Amphibiotic answered 6/12, 2022 at 12:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.