Gargoyle
Gargoyle

Reputation: 10324

Pass validator to child component

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.

Upvotes: 0

Views: 3252

Answers (1)

Amer
Amer

Reputation: 6706

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);
  // ...
}

Upvotes: 4

Related Questions