Как использовать debounceTime() и DifferentUntilChanged() в асинхронном валидаторе

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

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

Оказывается, что distinctUntilChanged() на самом деле не подходит для асинхронной проверки формы. Ссылки здесь и здесь.

funkid 01.06.2019 08:36
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
1
1 405
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что новый канал строится каждый раз, когда выполняется validatorFn, когда вы вызываете pipe() внутри validatorFn. Предыдущее значение не является захватом для работы Discinct или Debounce. Что вы можете сделать, так это настроить два 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))
  }
}

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

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

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

Я думаю, что debounceTime() следует называть до distinctUntilChanged(). А так как тип val в termDebouncer тривиально равен string, кажется, что вы можете использовать val.trim() непосредственно в первом map().

funkid 31.05.2019 17:29

Это отрывок из одного из моих проектов, где значение может быть number или string. Я скопировал и вставил некоторые и добавил некоторые. Да, вы правы на distinctUntilChanged()

Avin Kavish 31.05.2019 17:45

Я обнаружил, что хотя debounceTime() и distinctUntilChanged() теперь работают нормально, форма застревает в статусе PENDING. take(1) или first() не кажутся правильным способом исправить это, поскольку любой из них «завершит» наблюдателя и никогда не примет значение, выдаваемое из элемента управления после этого.

funkid 01.06.2019 05:32

нет, их можно использовать, потому что оператор validationEmitter.asObservable() вызывается каждый раз, когда проверяется ввод, и вы можете передать его туда.

Avin Kavish 01.06.2019 06:16

Вы правы, я неправильно надел take() трубу на termDebouncer. Теперь асинхронная проверка может быть завершена. Но здесь возникает другая ситуация дилеммы: если distinctUntilChanged() терпит неудачу, mockService не будет выполняться, поэтому ничего не будет передано validationEmitter, и форма снова застрянет в статусе PENDING.

funkid 01.06.2019 07:17

Я сделал изменение. Послушайте, я показал вам, как фиксировать и повторно использовать темы поведения, которые изначально вас сдерживали. Я не могу продолжать помогать вам учитывать все возможные сценарии. Это твоя работа. Например: это не будет работать, если есть сетевая ошибка. Эта ссылка объясняет все операторы, необходимые для работы наблюдаемых, остальное зависит от вас. Learnrxjs.io/операторы

Avin Kavish 01.06.2019 07:58

Другие вопросы по теме