4

I've built a simple form using Angular Reactive Forms. I have a custom Validator function, which checks the format of a phone number input. I want to allow an empty input if the users chooses not to enter a phone number, but then validate their entry if they enter one.

To achieve this, I subscribe to the value changes on the form control and use the setValidators method to toggle the validation if the field is empty or not:

phoneControl.valueChanges.subscribe(() => {
  if (phoneControl.value === "") {
    phoneControl.setValidators(null);
  } else {
    phoneControl.setValidators(this.phoneValidator());
  }
});

Please see the full code on StackBlitz.

The code works initially, form.valid is true. When I enter a number it turns on the validation and then successfully validates it according to my RegEx and shows an error.

However, when I empty the field, the error does not disappear and the form form is still invalid, even though my code should set the validator to null. You can see this in the StackBlitz example above.

I can't figure out why this is happening. Any ideas?

5 Answers 5

4

When dynamically setting validators, we need to call updateValueAndValidity(). Also remember to add emitEvent: false, that means that the valueChanges will not be triggered (to prevent looping). So change your code to:

phoneControl.valueChanges.subscribe(() => {
  if (phoneControl.value === "") {
    phoneControl.setValidators(null);
    // or
    // phoneControl.setValidators([]);
    // or as mentioned by Tilak
    // phoneControl.clearValidators();
  } else {
    phoneControl.setValidators(this.phoneValidator());
  }
  phoneControl.updateValueAndValidity({emitEvent: false});
});

Your StackBlitz

Sign up to request clarification or add additional context in comments.

1 Comment

Oh! So that's why the formControl was emitting random events! {emitEvent: false} with updateValueAndValidity! This helped. Made my code stable
2

I will recommend you to do this way -

if (phoneControl.value === "") {
        phoneControl.clearValidators();
} else {
        phoneControl.setValidators(this.phoneValidator());
}
phoneControl.updateValueAndValidity({emitEvent: false});

Clearing validator is a better way than setting it to null.

Comments

2

For validating Phone Number Input, you can use pattern validator of @rxweb/reactive-form-validators.

You just have to mention the phone number regex in your formControl like this:

ngOnInit() {
        this.userFormGroup = this.formBuilder.group({
            phoneNumber:['', RxwebValidators.pattern({expression:{'onlyPhoneNumber': /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/} })], 
        });
    }

For error message, you can bind the message in you app.component.ts like this:

ngOnInit(){
    ReactiveFormConfig.set({"validationMessage":{"onlyPhoneNumber":"Invalid phone number"}});
  }

This is the complete HTML code:

<div>
  <form  [formGroup]="userFormGroup">
    <div class="form-group">
      <label>Phone Number</label>
      <input type="text" formControlName="phoneNumber" class="form-control"  />
     <small class="form-text text-danger" *ngIf="userFormGroup.controls.phoneNumber.errors">{{userFormGroup.controls.phoneNumber.errors.onlyPhoneNumber.message}}<br/></small>  
    </div>
    <button [disabled]="!userFormGroup.valid" class="btn btn-primary">Submit</button>
  </form>
</div>

Here is the complete Component code

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms"
import { RxwebValidators } from '@rxweb/reactive-form-validators';

@Component({
    selector: 'app-pattern-add-validator',
    templateUrl: './pattern-add.component.html'
})
export class PatternAddValidatorComponent implements OnInit {
    userFormGroup: FormGroup

    constructor(
        private formBuilder: FormBuilder )
    { }

    ngOnInit() {
        this.userFormGroup = this.formBuilder.group({
            phoneNumber:['', RxwebValidators.pattern({expression:{'onlyPhoneNumber': /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/} })], 
        });
    }
}

Working Stackblitz

Comments

1

I faced the same "issue" many times. And it is somehow intended. It will do the same with "classic" validators (non-custom).

What I've been doing is something similar to this :

phoneControl.valueChanges.subscribe(() => {
  if (phoneControl.value === "") {
    phoneControl.reset();
    phoneControl.markAsPristine();
    phoneControl.markAsUntouched();
    phoneControl.updateValueAndValidity();
  } else {
    phoneControl.setValidators(this.phoneValidator());
  }
});

If you use only updateValueAndValidity() your form will say it is in valid state even when the value is empty.

Comments

0

It should just be fine by editing the validator itself:

  isValidPhoneNumber(phoneNumber: string) {
    if(phoneNumber === "")return true;
    return new RegExp(/^[ 0 ]{1,1}?[0-9- ]{9,15}$/).test(phoneNumber);
  }

and removing the subscription.

2 Comments

How does this solve it? Does the new RegExp allow it to be empty, as well as check the value if the user has entered some characters?
If the value is empty, then skip the Regexp validation and just return true, if not do the normal validation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.