angularform-controlangular-validator

How to get list of all validators of Angular FormControl?


I have a component with a custom Form Control. And in this component I need to get a list of validators from the parent component. I use Reactive forms everywhere, so I don't need to inject NG_VALIDATORS, I don't need custom validators, I need exactly the parent component's validators.

Some examples:

parent.component.ts

export class ParentComponent {

  public loginForm: FormGroup = this.fb.group({
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [
      Validators.required,
      Validators.minLength(6),
    ]),
  });
}

parent.component.html

<form [formGroup]="loginForm">
    <child-component type="email" formControlName="email"></child-component>
    <child-component type="password" formControlName="password"></child-component>
</form>

child.component.ts

export class ChildComponent implements ControlValueAccessor, AfterViewInit {

    valueControl = new FormControl('');  
    constructor( private injector: Injector ) { }

    ngAfterViewInit() {
      const ngControl = this.injector.get(NgControl);
      // here I need something like this: const validators = ngControl.control?.validatorsList;      
      this.valueControl.setValidators(validators);
    }

  // some ControlValueAccessor interface
}

child.component.html

<mat-form-field>
  <input
    matInput
    [type]=type
    [formControl]="valueControl"
  />
</mat-form-field>

I see a couple of acceptable solutions:

  1. Just pass a list of Validators with @Input to the child component like this:

parent.component.ts

export class ParentComponent {

  emailValidators = [Validators.required, Validators.email];

  public loginForm: FormGroup = this.fb.group({
    email: new FormControl('', emailValidators),
    password: new FormControl('', [
      Validators.required,
      Validators.minLength(6),
    ]),
  });
}

parent.component.html

<form [formGroup]="loginForm">
    <child-component [validators]=emailValidators type="email" formControlName="email"></child-component>
</form>

child.component.ts

export class ChildComponent implements ControlValueAccessor {
    
    @Input() validators;
    valueControl = new FormControl('');  
    constructor( private injector: Injector ) {
        this.valueControl.setValidators(this.validators);
    }

  // some ControlValueAccessor interface
}
  1. Or receive form control errors on the fly and set validators based on them, but this is an unjustified burden.

child.component.ts

export class ChildComponent implements ControlValueAccessor, AfterViewInit {

    valueControl = new FormControl('');  
    constructor( private injector: Injector ) { }
    ngControl.control?.statusChanges.subscribe(() => {
      // use ngControl.control?.errors
    });

  // some ControlValueAccessor interface
}

In connection with all the above I have a question: Are there best practices to solve this issue? And also why does Angular AbstractControl not provide such a function, but only has private variables like _rawValidators?


Solution

  • The property validator of an abstractControl is a FnValidator, so you can simply use

      valueControl = new FormControl('');
      ngControl = this.injector.get(NgControl);
    
      ngAfterViewInit() {
        this.valueControl.setValidators(this.ngControl?.control?.validator || null);
        this.valueControl.updateValueAndValidity();
      }
    

    NOTE: I put as property of the component the ngControl. In this way you can check the property of it

    a stackblitz

    NOTE related stackblitz:in the stackblitz it's necessary, when change "valueControl" call to the function this.onChange(value) to give value to the control.