Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular custom async validator not working

Here is the demo plnkr. I am trying to implement a custom async validator for a OTP input which has a text box and a verify button. I want to validate the input only when the user clicks on the verify OTP button or form submit button. Currently validation is happening on the text change event and it's not working. Form HTML:

<form [formGroup]="registrationForm" (ngSubmit)="registrationForm.valid && submitRegistration(registrationForm.value)" novalidate>
  <fieldset class="form-group">
    <label for="e-mail">Mobile</label>
    <div class="input-group">
      <input type="text" class="form-control" placeholder="Mobile" formControlName="mobile">
      <span class="input-group-btn">
        <button class="btn btn-secondary" type="button">Send OTP</button>
      </span>
    </div>
    <div class="form-text error" *ngIf="registrationForm.controls.mobile.touched">
      <div *ngIf="registrationForm.controls.mobile.hasError('required')">Mobile is required.</div>
    </div>
  </fieldset>
  <fieldset class="form-group">
    <label for="e-mail">Verify OTP</label>
    <div class="input-group">
      <input type="text" class="form-control" placeholder="OTP" formControlName="otp">
      <span class="input-group-btn">
        <button class="btn btn-secondary" (click)="veryOTPAsyn(otp)" type="button">Verify</button>
      </span>
    </div>
    <div class="form-text error" *ngIf="registrationForm.controls.otp.touched">
      <div *ngIf="registrationForm.controls.otp.hasError('required')">OTP is required.</div>
      <div *ngIf="registrationForm.controls.otp.hasError('invalidOtp')">OTP is invalid.</div>
    </div>
  </fieldset>
  <button class='btn btn-primary' type='submit' [disabled]='!registrationForm.valid'>Submit Registration Form</button>
</form>

Form Component:

export class ExampleFormComponent {
  registrationForm: FormGroup;

  constructor(public fb: FormBuilder) {
    // Example use of FormBuilder, FormGroups, and FormControls
    this.registrationForm = fb.group({
      mobile: ['', Validators.required],
      otp: ['', Validators.compose([Validators.required, this.veryOTPAsyn.bind(this)])],
      dob: ['', Validators.required],
      email: ['', Validators.compose([Validators.required,  emailValidator])],
      password: ['', Validators.required],
      confirmPassword: ['', Validators.required],
      firstName: ['', Validators.required],
      lastName: ['', Validators.required]
    }, {validator: matchingPasswords('password', 'confirmPassword')})

  }

  submitRegistration(value: Object): void {
    console.log(value);
  }

  veryOTPAsyn(otpControl: FormControl): Promise<any> {
    console.log(otpControl)
    console.log(otpControl.hasError('invalidOtp'))
    return new Promise<any>(
      (resolve, reject) => {
        setTimeout(() => {
          resolve({invalidOtp:true});
        }, 500);
      });
  }

}
like image 545
Saurabh Palatkar Avatar asked Jun 27 '17 16:06

Saurabh Palatkar


2 Answers

Try this :

 otp: ['', Validators.required, this.veryOTPAsyn.bind(this)]

Async validators go in 3rd argument

like image 169
Simrugh Avatar answered Oct 10 '22 16:10

Simrugh


I would consider to remove the custom validation and instead set / remove error when user clicks on button instead, since the custom validators will run when component is initialized, as well as whenever input value changes. So have your click event, and do something like this (pseudo code):

(click)="veryOTPAsyn(registrationForm.controls.otp)"

and TS:

veryOTPAsyn(ctrl: FormControl) {
  if(ctrl.value == 'hey') {
    ctrl.setErrors({'invalidOtp':true})
  } else {
    ctrl.setErrors({'invalidOtp':null})
  }
}

Forked PLUNKER.


EDIT:

But if you want to go the route of using the async validator, just add the validator as third argument, but then you don't need the 'validate' button.

otp: ['', [Validators.required], [this.veryOTPAsyn]]

or do it like it's done now after the release of v 5.0.0

otp: ['', {validators: [Validators.required], asyncValidators: [this.veryOTPAsyn]}]

PLUNKER (pre v.5.0.0, so the first option of marking async validator is used)

like image 45
AT82 Avatar answered Oct 10 '22 17:10

AT82