Как использовать debounceTime () и DifferentUntilChanged () в асинхронном валидаторе - PullRequest
1 голос
/ 30 мая 2019

Я хочу добавить debounceTime и distinctUntilChanged в свой асинхронный валидатор.

mockAsyncValidator(): AsyncValidatorFn {
    return (control: FormControl): Observable<ValidationErrors | null> => {
      return control.valueChanges.pipe(
        debounceTime(500),
        distinctUntilChanged(),
        switchMap(value => {
          console.log(value);  // log works here
          return this.mockService.checkValue(value).pipe(response => {
            console.log(response);  // log did not work here
            if (response) {
              return { invalid: true };
            }
            return null;
          })
        })
      );
  }

Приведенный выше код не работает, статус формы становится PENDING.
Но когда я используюtimer в этот ответ , код работает, но тогда я не могу использовать distinctUntilChanged.

return timer(500).pipe(
    switchMap(() => {
      return this.mockService.checkValue(control.value).pipe(response => {
        console.log(response);  // log works here
        if (response) {
          return { invalid: true };
        }
        return null;
      })
    })
  );

Я пытался использовать BehaviorSubject как

debouncedSubject = new BehaviorSubject<string>('');

и используйте его в AsyncValidatorFn, но все равно не работайте, как это:

this.debouncedSubject.next(control.value);
return this.debouncedSubject.pipe(
  debounceTime(500),
  distinctUntilChanged(), // did not work
                          // I think maybe it's because of I next() the value
                          // immediately above
                          // but I don't know how to fix this
  take(1), // have to add this, otherwise, the form is PENDING forever
           // and this take(1) cannot add before debounceTime()
           // otherwise debounceTime() won't work
  switchMap(value => {
    console.log(value); // log works here
    return this.mockService.checkValue(control.value).pipe(response => {
        console.log(response);  // log works here
        if (response) {
          return { invalid: true };
        }
        return null;
      }
    );
  })
);

1 Ответ

1 голос

Проблема в том, что новый канал создается каждый раз, когда validatorFn выполняется, когда вы вызываете pipe() внутри validatorFn. Предыдущее значение не является записью для дискинта или отказов в работе. Что вы можете сделать, это настроить два BehaviourSubjects внешне, termDebouncer и validationEmitter в моем случае.

Вы можете установить фабричный метод для создания этого валидатора и, тем самым, использовать его повторно. Вы также можете расширить AsyncValidator и создать класс с настройкой DI. Я покажу фабричный метод ниже.

export function AsyncValidatorFactory(mockService: MockService) { 
  const termDebouncer = new BehaviorSubject('');
  const validationEmitter = new BehaviorSubject<T>(null);
  let prevTerm = '';
  let prevValidity = null;

  termDebouncer.pipe(
        map(val => (val + '').trim()),
        filter(val => val.length > 0),
        debounceTime(500),
        mergeMap(term => { const obs = term === prevTerm ? of(prevValidity) : mockService.checkValue(term);
          prevTerm = term; 
          return obs; }),
        map(respose => { invalid: true } : null),
        tap(validity => prevValidity = validity)
    ).subscribe(validity => validationEmitter.next(validity))


  return (control: AbstractControl) => {
    termDebouncer.next(control.value)
    return validationEmitter.asObservable().pipe(take(2))
  }
}

Редактировать : Этот фрагмент кода взят из варианта использования, отличного от проверки угловых форм (точнее, для виджета React search), операторам конвейера, возможно, потребуется изменить в соответствии с вашим вариантом использования.

Edit2 : take(1) или first(), чтобы гарантировать, что наблюдаемое завершается после отправки сообщения проверки. asObservable() обеспечит создание новой наблюдаемой при следующем вызове. Вы также можете пропустить asObservable() и просто pipe(), так как оператор канала разветвляет асинхронный конвейер и создает оттуда новую наблюдаемую область. Возможно, вам придется использовать take(2), чтобы обойти тот факт, что поведенческий объект является состоянием и содержит значение.

Edit3 : Используйте карту слияния, чтобы учесть тот факт, что distinctUntilChanged() приведет к тому, что наблюдаемое не испустит и не завершится.

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