Angular2: Find out if FormControl has required validator?

AngularFormsTypescriptValidation

Angular Problem Overview


does someone know a way to find out for an Angular2 FormControl if the required validor is registered for the control.

this.form = builder.group({name: ['', Validators.required]};

Can I then query the this.form.controls['name'] control if it is a required field? I know I can check if it is valid, but that's not what I want.

Kind regards, Marc

Angular Solutions


Solution 1 - Angular

This function should work for FormGroups and FormControls

  export const hasRequiredField = (abstractControl: AbstractControl): boolean => {
	if (abstractControl.validator) {
		const validator = abstractControl.validator({}as AbstractControl);
		if (validator && validator.required) {
			return true;
		}
	}
	if (abstractControl['controls']) {
		for (const controlName in abstractControl['controls']) {
			if (abstractControl['controls'][controlName]) {
				if (hasRequiredField(abstractControl['controls'][controlName])) {
					return true;
				}
			}
		}
	}
	return false;
};

Solution 2 - Angular

While there is no Angular API to directly find if the required validator is set for a particular field, the roundabout way to achieve this is like as below:

import { NgForm, FormControl } from '@angular/forms';

const isRequiredControl = (formGroup: NgForm, controlName: string): boolean => {
    const { controls } = formGroup
    const control = controls[controlName]
    const { validator } = control
    if (validator) {
        const validation = validator(new FormControl())
        return validation !== null && validation.required === true
    }
    return false
}

I have tested this and this triggers only if the Validator.Required validator is added to the particular FormControl.

Solution 3 - Angular

Angular now provides hasValidator with which you can check if a formcontrol has a sync validator. If you happen to need to check if it has an async validator, that also exist with hasAsyncValidator.

In your case when you want to check if the formcontrol is required, you can do:

this.form.get('name').hasValidator(Validators.required);

Solution 4 - Angular

There is no method how to check validators or get all validators: https://github.com/angular/angular/issues/13461

@fairlie-agile solution is pretty clever. But I think we have to use empty FormControl because we need fire required validator and this.group.controls[this.config.name] could be already initialized with some value.

ngOnInit() {
    let formControl = this.group.controls[this.config.name];
    let errors: any = formControl.validator && formControl.validator(new FormControl());
    this._required = errors !== null && errors.required;
}

Solution 5 - Angular

I have a similar issue. For now, I'm using this:

  import { Attribute } from '@angular/core';

  // "Kind-of" hack to allow "pass-through" of the required attribute
  constructor(@Attribute('required') public required) {
    // call super here if the component is an ancestor
  }

I'm really puzzled as to why properties like "disabled" are included in FormControl, but not "required".

Solution 6 - Angular

One way to do this is to check if the control is valid when the form is loaded to see if it has the required error (which it will if the field is empty).

This won't work for other validators, such as minLength, as they are not fired until the control is changed

export class FormInputComponent implements Field, OnInit {
  private _required: boolean;
  config: FieldConfig;
  group: FormGroup;

    /** Readonly properties. */
  get required(): boolean { 
    return this._required;
  }

  ngOnInit() {
    var _validator: any = this.group.controls[this.config.name].validator && this.group.controls[this.config.name].validator(this.group.controls[this.config.name]);
    this._required = _validator && _validator.required;
  }
}

Solution 7 - Angular

I solved a similar problem with this:

get lastPriceField(): AbstractControl {
    return this.form.get('last_price');
}

private checkIfFieldHasValidatorRequired() {    
    const validators = (this.lastPriceField as any)._rawValidators;
    this._lastPriceIsRequired = validators && validators.some(item => item.name === "required");
}

Solution 8 - Angular

With the assumption that the only error registered initially is the required error

// in your component you'll have access to `this.myFormGroup`
const formGroup = {
  controls: {
    email: {
      errors: {
        required: true
      }
    },
    username: {
      errors: null
    }    
  }
}

// required by default
let required = {
  email: '*',
  username: '*',
};

// in `ngOnInit`
required = Object.entries(formGroup.controls)
  .map(([key, control]) => [key, control.errors])
  .filter(([, errors]) => !errors)
  .map(([key]) => [key, ''])
  .reduce((_required, [key, isRequired]) => Object.assign(_required, { [key]: isRequired }), required)

// in your template you may write `<label>Email{{ required.email }}</label>`
console.log(required)

Solution 9 - Angular

A short version. First, create an empty control to avoid doing it constantly in the method.

  private readonly emptyFormControl = new FormControl();

Then check in the method. A control is mandatory if it's enabled and its validator gains an 'required' error when validating an empty control.

  private isMandatory = (fc: FormControl): boolean =>
    fc.enabled && !!fc.validator && !!fc.validator(this.emptyFormControl)?.required;

This is only applicable to the FormControl object. If you need to support FormGroup or / and FormArray, you need to add recursion to the checking method.

Solution 10 - Angular

I don't know what's the exact way is to check if control has required validator or not.

But workaround could be this, Whenever control has required validator, it adds validator() function to that control.

eg.

<input type="text" formControlName="firstname">

constructor(private formBuilder: FormBuilder){
    this.registerForm = this.formBuilder.group({
        firstname: ['', Validators.required]     //<<<===one required validation on firstname control
    });

    console.log(this.registerForm.controls.firstname.validator.length);
                                                 //<<<===this will return 1.
   });

}

In above code, length of validator is one(1)


  console.log(this.registerForm.controls.firstname.validator.length);
                                                 //this will return exception

this line will return one. If no validator is attached then firstname will not have validator() function so in that case I'll give an exception.

DEMO : https://plnkr.co/edit/I7b5JNAavmCJ6Py1eQRr?p=preview

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarcView Question on Stackoverflow
Solution 1 - AngularMarcel TinnerView Answer on Stackoverflow
Solution 2 - AngularAragornView Answer on Stackoverflow
Solution 3 - AngularAT82View Answer on Stackoverflow
Solution 4 - Angularvlapo21View Answer on Stackoverflow
Solution 5 - AngularDave NottageView Answer on Stackoverflow
Solution 6 - AngularFairlie AgileView Answer on Stackoverflow
Solution 7 - AngularIsai David Vélez De LeónView Answer on Stackoverflow
Solution 8 - AngularMateja PetrovicView Answer on Stackoverflow
Solution 9 - AngularVasily IvanovView Answer on Stackoverflow
Solution 10 - AngularNikhil ShahView Answer on Stackoverflow