Angular form control validation when using ControlValueAccessor

3.4k views Asked by At

In the parent component I define the form.

  cartForm = new FormGroup({
    company: new FormControl('', [Validators.required]),
    costCenter: new FormGroup({
      account: new FormControl('', [Validators.required]),
      activity: new FormControl('', [Validators.required, Validators.maxLength(5), Validators.minLength(5)]),
      project: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(10)]),
    })
  });

then the formControl project is used in a child component. In this component I need a custom validation and I can achive that by just adding

  validate(control: AbstractControl): ValidationErrors | null {
    // To my validation here
  }

And that works except that the Validators specified in the parent componet are overwritten.

Second approach would be to create a custom validator class.. but then I can't get the @Input from the component..?

Update: This is the component

@Component({
  selector: 'ssp-project',
  templateUrl: './project.component.html',
  styleUrls: ['./project.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectComponent implements ControlValueAccessor, OnInit {

  @Input('companyNumber') set companyNumber(companyNumber: string) {
    this._companyNumber = companyNumber;
  }
  private _companyNumber: string;

  constructor(
    @Optional() @Self() public ngControl: NgControl,
  ) {
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }

  onTouched = (_value?: any) => { };
  onChanged = (_value?: any) => { };

  writeValue(val: string): void {
    if (val) {
      this.ngControl.control?.setValue(val);
    }

  }

  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  registerOnValidatorChange(fn: any): void {
    this.onChanged = fn;
  }

  ngOnInit() {
    // this.ngControl.control.setValidators([this.validate.bind(this)]);
    this.ngControl.control.setValidators(this.ngControl.control.validator ? [this.ngControl.control.validator, this.validate] : this.validate);
    this.ngControl.control.updateValueAndValidity();
  }

  validate(control: AbstractControl): ValidationErrors | null {

    if (this._companyNumber && control.value) {
      return this._costCenterService
        .validateProject(control.value, this._companyNumber)
        .pipe(
          debounceTime(3000),
          map(projectIsValid => {
            return { invalid: !projectIsValid };
          })
        ).subscribe();

    }
  }
}

1

There are 1 answers

6
evilstiefel On BEST ANSWER

You can add a validator to the list of existing validators using the validator property of the FormControl

control.setValidators(control.validator ? [ control.validator, this.validate ] : this.validate);

Edit: since the code was added in the question, the focus has shifted to async validators. E.g.:

control.setAsyncValidators(
  control.asyncValidator
    ? [control.asyncValidator, this.asyncValidator]
    : this.asyncValidator
);

// definition of asyncValidator
asyncValidator(ctrl: AbstractControl): Observable<ValidationErrors | null> {
  // Whatever your validation does asynchronously
  return of(null);
}