Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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;
}
like image 538
user3595026 Avatar asked Dec 05 '17 19:12

user3595026


1 Answers

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

like image 71
AT82 Avatar answered Nov 15 '22 08:11

AT82