1
votes

I am trying to implement a ValidatorFn interface, however, I am getting an issue when trying to use the 'controls' property on a formGroup of type 'AbstractControl', the error is: "Property 'controls' does not exist on type 'AbstractControl'", I originally attempted to set formGroup as type 'FormGroup', however, this brought upon an array of errors. My ValidatorFn interface is as follows:

1. import { FormGroup, ValidatorFn, AbstractControl } from '@angular/forms';
2. export function requireCheckboxesToBeCheckedValidator(minRequired = 1): ValidatorFn {
3.   return function validate (formGroup: AbstractControl) {
4.     let checked = 0;
5. 
6.     Object.keys(formGroup.controls).forEach(key => {
7.       const control = formGroup.controls[key];
8. 
9.       if (control.value === true) {
10.         checked ++;
11.       }
12.     });
13. 
14.     if (checked < minRequired) {
15.       return {
16.         requireOneCheckboxToBeChecked: true,
17.       };
18.     }
19. 
20.     return null;
21.   };
22. }

The error occurs when using the controls property on lines 6 and 7. Is there a workaround to this when using a formGroup of type AbstractControl?

1
Since FormGroup inherits after AbstractControl, if you know your validator will ALWAYS be applied to a FormGroup you can change the type in the function (e.g. replace formGroup: AbstractControl with formGroup: FormGroup). Otherwise you can write a typeguard and use it within your validator, either returning null or throwing an exception in case of a type mismatch. - TotallyNewb
@TotallyNewb Would you not maybe have a good reference to provide with regards to implementing a typeguard in the situation above? I am not familiar with type guards? - SeventhWarhawk

1 Answers

1
votes

You can add a type guard to make sure you have a FormGroup, and you can also handle the case when you have just a FormControl or a FormArray.

export function requireCheckboxesBeCheckedValidator(minRequired = 1): ValidatorFn {
  return (formControl: AbstractControl) => {
    let ctrls: AbstractControl[];
  
    if (formControl instanceof FormGroup) {
      ctrls = Object.values(formControl.controls);
    } else if (formControl instanceof FormArray) {
      ctrls = formControl.controls;
    } else {
      ctrls = [ctrls];
    }
            
    const checked = formControl.controls.filter(({ value }) => value === true);

    return checked < minRequired ? { requireOneCheckboxToBeChecked: true } : null;
  }
}