Angular reactive forms - validate on change and blur both

Viewed 1679

I am using angular reactive forms in my application. I have requirement that some of the validations need to be run on change (default behavior of angular form validation), and some other validations need to be run only on blur for performance .

Basically, when user starts typing in a text box, I want to perform client side validation and show errors as user types. Also, I need to to server side business validations on the text box, which I want to do on blur.

The question is: can we configure angular reactive forms so that some validations are run on change and some other on blur. Or do we have some other alternatives?

2 Answers

Yes you can, since Angular 5 you can do this:

Example:

this.email = new FormControl(null, {
   validators: Validators.required,
   updateOn: 'blur'
});

I encountered the same issue, since I couldn't find anyway to set different update strategies on the same input field, I found a nice workaround, I explain it in the example below:

In a signup form, I had the input field for an email for which I needed two validators with two different strategies:

  • One to check the email is actualy formatted as an email, with a sync validator on client side with the built-in validator Validators.email, I want this one to be with updateOn: "change" so it is checked as the user is typing

  • Another to check in the DB if the email doesn't already exists with an async API CALL, I want this one to be done with updateOn: "blur" (when the user leaves the input) and only if the email is well formatted as an email (so when first validator is valid).

The form group creation in my ts file where the first validator is declared, no need to set updateOn: "change" since it's the value by default:

  public myForm: FormGroup;
  private emailSubscription: Subscription;

  this.myForm = this.fb.group({
    // ... some other inputs
    email : ["", {validators: Validators.email}]  
  })

And I made the async validator in a subcription to the form changes in the AfterViewInit after a filter on emailControl.touched that equals the updateOn: "blur", then , not relevant here, it checks email doesn't exist with an API CALL

ngAfterViewInit() {
  // Gets input control
  const emailControl = this.signupForm.get("email");

  // Subscribes to the input control's valueChanges Observable
  this.emailSubcription = emailControl.valueChanges
    .pipe(
      // Checks only when email input is left and valid (so when
      // the "genuine" Vaildators.email is verified
      filter((checkedEmail) => emailControl.touched && emailControl.valid),

      // API CALL checking if email exists, we use switchMap to end previous
      // call if user keep typing and result has not been returned yet
      switchMap((inputEmail) => {
        // variable made to send data to server with API CALL, 
        // not relevant here
        const checkedEmail = { checkedEmail: inputEmail.toLowerCase() };
        // API call to check email exists, not relevant here, 
        // could be any async method 
        return this.http.post("/api/user/emailExists", checkedEmail);
      })
    )
    .subscribe((emailExists: boolean) => {
      // Sets an error on the input control if the email exists,
      // so the error can be then processed as needed (for example
      // to show a div in template or use with mat-error of the input)
      if (emailExists === true) {
        emailControl.setErrors(
          { emailNotAvailable: true },
          { emitEvent: false }
        );
      }
    });
  }

At last, do not forget to unsubscribe the observable:

public ngOnDestroy() {
  if (this.emailSubscription) this.emailSub.unsubscribe();
}
Related