Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get validators present in FormGroup/FormControl

I'm using Material 2 in my app, but in this question I want to solve a problem specifically with Input.

As you can see in API Reference there's a property bind called required, which shows as asterisk in the placeholder.

So, I'm wondering if there's a way to check if the form control has an specific validator in Angular, because I really don't want to set manually for each input [required]="true/false"

I read the AbstractControl docs and I didn't find anything about it. I've encountered the hasError method (which ironically isn't documented in nowhere ... neither in FormGroup nor in FormControl nor in AbstractControl), however this is not what I'm looking for. It just checks if the form control has the error, but as you may have read, I want to check if the control has some specific validators...

Some code:

<md-input-container>   <input placeholder="Placeholder"           mdInput [formControl]="anyCtrl"           [required]="anyCtrl.hasValidator('required')"> <!-- something like this --> </md-input-container> 

I hope the question is clear enough. Thanks in advance.

like image 571
dev_054 Avatar asked May 08 '17 00:05

dev_054


People also ask

How will you add validators in FormControl?

We can add Validators dynamically using the SetValidators or SetAsyncValidators. This method is available to FormControl, FormGroup & FormArray. There are many use cases where it is required to add/remove validators dynamically to a FormControl or FormGroup.

How do I get data from FormControl?

Get and Set Value To fetch the value of a form control, we have to use value property on the instance of FormControl in our class. In the same way we can fetch the value in HTML template.

Is it possible to define validators in form builder?

FormBuilder allows us to explicitly declare forms in our components. This allows us to also explicitly list each form control's validators. In our example we are going to build a small form with three inputs, user name, email and profile description. We will start with looking at our app.


2 Answers

Angular doesn't really provide a great, clean way to do this, but it is possible. I think the validators are stored in a service that is injected into the FormBuilder(NG_VALIDATORS), and I'm going to look into hijacking that service or injecting it into a component, but for now this will work:

The docs and the source show a validator member on AbstractControl typed to ValidatorFn. ValidatorFn unfortunately simply has a null typing, so we can't see what's going on. However, after poking through the generated source and probing an app, it seems we can pass this validators method a control parameter, which will return an object of all validators present on that control, regardless of whether or not it's passing.

Strangely, this only works on the FormControl itself and not the FormGroup (on the FormGroup, the validators member is not a function and was always null in my testing). The compiled JS says this function takes a control parameter; I've tried passing in FormControl references but as far as I can tell it will just return the validators on the control as long as this parameter is not null.

Getting validators on a FormControl

// in the constructor this.myForm = this.formBuilder.group({   'anyCtrl': ['', Validators.required],   'anotherCtrl': ['', Validators.compose([Validators.required, Validators.email])] });  // later on  let theValidators = this.myForm.controls['anyCtrl'].validator(''); console.log(theValidators) // -> {required: true};  let otherValidators = this.myForm.controls['anotherCtrl'].validator(''); console.log(otherValidators); // -> {required: true, email: true} 

Making it easier to grab:

public hasValidator(control: string, validator: string): boolean {   return !!this.myForm.controls[control].validator(control).hasOwnProperty(validator);  // returns true if control has the validator } 

and in your markup:

<md-input-container>   <input placeholder="Placeholder"           mdInput [formControl]="anyCtrl"           [required]="hasValidator('anyCtrl', 'email')"> </md-input-container> 

Special case for Validators.required

The required validator has a shortcut. The [required] binding is actually an instance of the RequiredValidator directive (line 5022 of source/forms.js). This directive actually will add the required Validator to the FormControl it's on. It's equivalent to adding Validators.required to the FormGroup upon initialization. So, setting the bound property to false will remove the required Validator from that control and vice versa...either way, the directive affects the FormControl.required value, so binding it to a property that it changes won't really do much.

The only difference is that the [required] directive adds the asterisk to the placeholder while Validators.required does not.

I'm going to keep looking into NG_VALIDATORS, but I hope this helps for now!

like image 89
joh04667 Avatar answered Sep 23 '22 08:09

joh04667


This answer is a continuation of @joh04667's. They wrote:

public hasValidator(control: string, validator: string): boolean {   return !!this.myForm.controls[control].validators(control).hasOwnProperty(validator);  // returns true if control has the validator } 

However there is no AbstractControls.validators() method. I'm assuming AbstractControls.validator() was meant.

The hasValidator() method only works for validators that 'fail' (eg. a required validator on a control with the value '' (empty)). Since if they pass they return null. A way around this would be to set the value so that it always fails and restore that afterwards.

public hasValidator(control: string, validator: string): boolean {     let control: AbstractControl = this.myForm.controls[control];     let lastValue: any = control.value;     switch(validator) {         case 'required':             control.setValue('');  // as is appropriate for the control         case 'pattern':             control.setValue('3'); // given you have knowledge of what the pattern is - say its '\d\d\d'         ....     }     let hasValidator: boolean = !!control.validator(control).hasOwnProperty(validator);      control.setValue(lastValue);     return hasValidator; } 

And this is pretty horrible. It begs the question - Why is there no AbstractControl.getValidators(): ValidatorFn[]|null?

What is the motivation in hiding this? Perhaps they are worried someone might put in their code:

... secretPassword: ['', [Validators.pattern('fjdfjafj734738&UERUEIOJDFDJj')] ... 
like image 44
HankCa Avatar answered Sep 24 '22 08:09

HankCa