7
votes

I'm very simply trying to validate a control if its value matches a value within a FormArray.

I want to be clear that I do not want to validate the form or a FormGroup or a FormArray. This question is for learning how to pass parameters to a validator function and the validation of the addValue control.

Here is what I have in my custom validation service:

public form: FormGroup = this.fb.group({
    addValue: this.fb.control(null, [this.validatorService.duplicate(this.form.get('values'))]),
    values: this.fb.array([])
});

And the validator function

public duplicate(values: FormArray): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
        for (let i = 0, j = values.length; i < j; i++ ) {
            if (control.value === values[i].value) {
                return { 'duplicate': true };
            }
        }
        return null;
    };
}

At this point I get an error where I add the validator with the FormArray as an argument:

Argument of type 'AbstractControl' is not assignable to parameter of type 'FormArray'. Type 'AbstractControl' is missing the following properties from type 'FormArray': controls, at, push, insert, and 5 more.ts(2345) (property) FormGroup.controls: { [key: string]: AbstractControl; }

Can someone show me how to send a FormArray into a validator function?

Here is a Stackblitz of the validator NOT getting the FormArray

2

2 Answers

2
votes

AbstractControl is the base class, simply cast you it:

public form: FormGroup = this.fb.group({
    addValue: this.fb.control(null, [this.validatorService.duplicate(this.form.get('values') as FormArray)]),
    values: this.fb.array([])
});

you could also sends values directly:

this.validatorService.duplicate(this.form ? this.form.get('values').value : []);

and

public duplicate(values: string[]): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
        for (let i = 0, j = values.length; i < j; i++ ) {
            if (control.value === values[i]) {
                return { 'duplicate': true };
            }
        }
        return null;
    };
}

NEW ANSWER

Instead add a form Validator:

this.form = this.fb.group({
  addValue: this.fb.control(null),
  values: this.fb.array(['test2', 'test3'])
}, { validator: this.validatorService.duplicate2 });

using this code:

public duplicate2(control: AbstractControl): ValidationErrors | null {
    const newValue = control.get('addValue') ? control.get('addValue').value : null;
    const values = control.get('values') ? control.get('values').value : [];

    console.log("1 " + newValue);
    console.log(values);
    for (let i = 0, j = values.length; i < j; i++ ) {
              if (newValue === values[i]) {
                  return { 'duplicate2': true };
              }
          }
          return null;          
  }

and also change your validation:

<app-validator [control]="form"></app-validator>

see https://stackblitz.com/edit/send-validator-formarray-l8j7ys

you can pass the field names as parameter to the validator.

-2
votes

You are trying to access to a FormArray, which is not instantiated yet:

    this.form = this.fb.group({
  addValue: this.fb.control(null, [this.validatorService.duplicate(this.form ? this.form.get('values').value : []), Validators.minLength(2)]),
  values: this.fb.array([])
});

to solve this you have to options:

1) instantiate the FormArray first
2) Add the validator after instantiating the FormGroup