Angular2 Generi c Asyn c Валидатор в Реактивных формах - PullRequest
2 голосов
/ 31 марта 2020

У меня есть реактивная форма и элемент управления формы, который должен быть уникальным:

this.form = new FormGroup({
  name: new FormControl(this.initialValue, [
    Validators.required,
  ], this._uniqueNameValidator.bind(this)),
});

Я написал asyn c валидатор, который вызывает метод из моей службы приложений:

private _uniqueNameValidator(control: FormControl) {
    const value = control.value.trim();
    if (value === '' || value === this.initialValue) {
      return of(null);
    }
    control.markAsTouched();
    return timer(500).pipe(
      switchMap(() => {
        return this._appService.validateName(value, this.selectedGroupId);
      }),
      map(response => {
        return response ? {nameTaken: true} : null;
      }),
      catchError(() => {
        return of(null);
      })
    );
 }

Метод обслуживания - это простой http GET, который возвращает значение boolean:

validateName(name: string, groupId: number): Observable<boolean> {
 // ...
 return this._http.get<boolean>(url);
}

Рабочий пример: https://stackblitz.com/edit/angular-8kedup

Теперь, в моем приложении есть много других форм с элементами управления, которые должны быть уникальными. Уникальность проверяется asyn c, но в других сервисных методах и в других маршрутах. Я не хочу копировать код для валидатора в каждый компонент, для которого нужна уникальность asyn c валидатор.

Проблема : я хочу написать универсальный c валидатор, который принимает в качестве параметров служебный метод, который должен быть вызван для определения уникальности значения, и начальное значение формы.

Я написал следующую функцию:

export function UniqueAsyncValidator(fn: any, initialValue: string): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    const value = control.value.trim();
    if (value === '' || value === initialValue) {
      return of(null);
    }
    control.markAsTouched();
    return timer(500).pipe(
      switchMap(() => {
        return fn(value);
      }),
      map(response => {
        return response ? {nameTaken: true} : null;
      }),
      catchError(() => {
        return of(null);
      })
    );
  };
}

И определение формы:

    this.form = new FormGroup({
      name: new FormControl(this.initialValue, [
        Validators.required,
        ], UniqueAsyncValidator(this._appService.validateName, this.initialValue).bind(this)),
   });

Кажется, что метод службы вызывается, но контекст (this) в методе службы не определен:

validateName(name: string, groupId: number): Observable<boolean> {
  // ....
  console.log( 'this: ', this );  // -> returns undefined 
  return this._http.get<boolean>(url);
}

Пример: https://stackblitz.com/edit/angular-sbbgc8

Спасибо!

1 Ответ

2 голосов
/ 31 марта 2020

Вы не делаете один и тот же вызов функции в обеих версиях. Ваша сервисная функция ожидает groupId. Вы должны вызвать службу с этим аргументом в вашем обратном вызове.

const uniqueValidator = UniqueAsyncValidator(
  value => this._appService.validateName(value, this.selectedGroupId), 
  this.initialValue).bind(this);

this.form = new FormGroup({
  name: new FormControl(this.initialValue, [
    Validators.required,
  ], uniqueValidator)
});

Когда вы создаете экземпляр UniqueAsyncValidator, вы передаете обратный вызов. Это позволяет вам повторно использовать logi c внутри валидатора и вводить функцию, которая будет выполнять вызов.

value => this._appService.validateName(value, this.selectedGroupId)

Это синтаксис функции стрелки. Это просто объявление анонимной функции, которая принимает один параметр - value (это передается вашим валидатором) - и возвращает this._appService.validateName(value, this.selectedGroupId).

DEMO: https://stackblitz.com/edit/angular-ynmzyu

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...