0

I'm using an angular child component that implements ControlValueAccessor with reactive forms. The child component is essentially just wrapping another component with a few extra things. So in the parent, when I do this:

<app-child formControlName="foo"></app-child>

the form group knows that foo is required. How, in the child component, do I determine that it's required? I need to add the required attribute in the HTML to the component being wrapped, essentially.

Gargoyle
  • 9,590
  • 16
  • 80
  • 145

1 Answers1

3

If the child component should be always required, then you can implement the Validator interface, and provide the NG_VALIDATORS token in the child component, to sync the invalid state with the parent form-group.

You can try the following to achieve that in this case:

@Component({
  // ...
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ChildComponent),
      multi: true,
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => ChildComponent),
      multi: true,
    },
  ],
})
export class ChildComponent implements ControlValueAccessor, Validator {
  innerCtrl: FormControl; // Use it to access the inner component value and invalid state.
  onValidationChange: () => void;

  validate(control: AbstractControl): ValidationErrors | null {
    if (this.innerCtrl.invalid) {
      return { invalid: true };
    } else {
      return null;
    }
  }

  registerOnValidatorChange?(fn: () => void): void {
    this.onValidationChange = fn;
  }

  // ...
}

Otherwise, if the child component shouldn't always be required, then you can inject the NgControl from the child component, to check if it has the required validator or not, using the AbstractControl.hasValidator() API which has been introduced in Angular 12.2.0 (Like how MatInput shows required asterisk when using required validator)

You can try the following to achieve that in this case:

@Component({
  // ...
})
export class ChildComponent implements ControlValueAccessor {
  get required(): boolean {
    return this.ngControl?.control?.hasValidator(Validators.required);
  }

  constructor(@Optional() @Self() private ngControl: NgControl);
  // ...
}
Amer
  • 6,162
  • 2
  • 8
  • 34
  • It’s not always required. That’s why I need to know whether the form said it was required or not. – Gargoyle Sep 18 '22 at 20:04
  • Well, I updated my answer to include the other case. Please check. – Amer Sep 19 '22 at 11:33
  • 1
    Perfect, thank you! For anyone else who uses this solution, remember you have to remove the `providers` piece and in the constructor add `this.ngControl.valueAccessor = this` – Gargoyle Sep 19 '22 at 16:00
  • tsk. Seems like i am not using Angular 12 as hasValidator() doesnt exist) – chitgoks Jul 10 '23 at 13:18