1
votes

Firstly i have this async validator for angular for password validation and I'm trying to make a delay for the message in html but it doesnt seem to work, how do i need to call it to work.I checked via console.log(control) in the function and it returns the expected result but it still appears instantly on how i called it in HTML code.

I will put sample code here. Here i make the form with the validators.

constructor(fb: FormBuilder)
  {
    this.form = fb.group({
      password: ['', Validators.required,this.asyncValidator],
      confirmPassword: ['', Validators.required,this.asyncValidator]
    })
  }

Here is the validation function.

asyncValidator(control:FormControl):any{
    return new Promise(
      (resolve) =>{
        setTimeout(() => {
          if((control.password).length < 6 && (control.password).length > 1)
              console.log("true");
          console.log("false");
          resolve(null);
        },2000);
      }
    );
 }

Here is the HTML code that i use in the page to see the delayed message(that doesn't work).

<div class="alert alert-danger"
  *ngIf="asyncValidator(this.form.controls.password)">Password too short</div>

How do i need to use the async validator so my message in HTML appears with a 2 secs delay?

1

1 Answers

0
votes

You seem to have misunderstood what async validators do and how they're used and written. Hence, there are a lot of issues with your implementation. Here's how you can fix them.

1. Get rid of the code in your constructor and move it to ngOnInit:

ngOnInit() {
  this.form = this.fb.group({
    password: ['', [Validators.required], [this.asyncValidator.bind(this)]],
    confirmPassword: ['', [Validators.required], [this.asyncValidator.bind(this)]]
  });
}

Rationale: constructors are supposed to be skinny according to Misko Hevery

Experienced developers agree that components should be cheap and safe to construct.

And, the async validators, are passed as the third argument to a FormControl

Also, since the async validators are functions that are going to be called by Angular and not by us, we need to set the context of this explicitly by calling bind(this) on the async validator function.

2. Now, the promise returned by the asyncValidator, should resolve to null in case of no error and an error object in case of an error:

asyncValidator(control: FormControl): any {
  return new Promise(
    (resolve) => {
      setTimeout(() => {
        if ((control.value).length < 6 && (control.value).length > 1)
          resolve(null);
        else
          resolve({
            shortPassword: true
          });
      }, 2000);
    }
  );
}

3. Create a function that is going to return a boolean based on whether a FormControl is touched and has an error that you're returning from your asyncValidator function:

getFormControlError(controlName) {
  const control = this.form.get(controlName);
  return control.touched && control.errors && control.errors.shortPassword;
}

Rationale: This is something that we'll be using in our template.

4. Update your template to show the error only if the input field is touched and has that error:

<form [formGroup]="form">
  Password: <input type="text" formControlName="password">
  <div class="alert alert-danger"
  *ngIf="getFormControlError('password')">Password too short</div>

  <br><br>

  Confirm Password: <input type="text" formControlName="confirmPassword"> 
  <div class="alert alert-danger"
  *ngIf="getFormControlError('confirmPassword')">Password too short</div>
  <br> 
  <br>
</form>

Here's a Working Sample Stackblitz for your ref.