Проверка элемента управления угловой формы при использовании ControlValueAccessor

В родительском компоненте я определяю форму.

  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)]),
    })
  });

тогда formControl project используется в дочернем компоненте. В этом компоненте мне нужна настраиваемая проверка, и я могу добиться этого, просто добавив

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

И это работает, за исключением того, что Validators, указанные в родительском компоненте, перезаписываются.

Второй подход - создать собственный класс валидатора ... но тогда я не могу получить @Input из компонента ...?

Обновление: это компонент

@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();

    }
  }
}


person Mackelito    schedule 14.10.2020    source источник


Ответы (1)


Вы можете добавить валидатор в список существующих валидаторов, используя свойство validator объекта FormControl.

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

Изменить: поскольку код был добавлен в вопрос, акцент сместился на асинхронные валидаторы. Например.:

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);
}
person evilstiefel    schedule 14.10.2020
comment
Похоже, это может сработать .. но когда я тестирую, данные @Input исчезли .. :( - person Mackelito; 14.10.2020
comment
Я обновил свой вопрос, чтобы он также содержал код компонента - person Mackelito; 14.10.2020
comment
Решил ... просто делал это неправильно :) thx! - person Mackelito; 14.10.2020
comment
Последующий вопрос .. validate () возвращает Observable .. но как на него подписаться? `` `` validate (control: AbstractControl): Observable ‹ValidationErrors | null ›{if (this._companyNumber && control.value) {return this._costCenterService .validateProject (control.value, this._companyNumber) .pipe (map (projectIsValid =› {if (projectIsValid) {return null;} else {return {validProject: projectIsValid};}})); }} `` ` - person Mackelito; 14.10.2020
comment
Итак, увидев ваш код сейчас, вам действительно нужно вместо этого использовать setAsyncValidators и напрямую возвращать наблюдаемое (а не подписку, поэтому не вызывайте subscribe() самостоятельно). Ознакомьтесь с официальной документацией по асинхронным валидаторам. - person evilstiefel; 14.10.2020
comment
Yepp .. увидел проблему и заставил ее работать вчера вечером! :) спасибо за вашу помощь! - person Mackelito; 15.10.2020