Пользовательский асинхронный валидатор Angular не отображает ошибку

Мой асинхронный валидатор не работает, когда я действительно выполняю асинхронный вызов (но работает, когда я имитирую его с помощью Observable.of(result). html выглядит так:

   <div class="form-group">
          <label for="emailAddress" class="control-label">Email</label>
          <input type="email"
                 id="emailAddress"
                 class="form-control"
                 [(ngModel)]="parent.EmailAddress"
                 name="emailAddress"
                 [ngModelOptions]="{updateOn: 'blur'}"
                 appDuplicateEmailValidator
                 #emailAddress = "ngModel">
        </div>

Речь идет о валидаторе appDuplicateEmailValidator.

Код проверки выглядит так:

  public validate(control: AbstractControl): Observable<ValidationErrors | null> {
    // return observableOf({ 'isDuplicateEmail': true });

    return this.checkForDupeUserAndGetValidationState(emailAddress);
  }

  private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
    const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
      this.adminService.getUser(emailAddress).subscribe(
        (res: any) => {
          const validationState = !!res ? observableOf({ 'isDuplicateEmail': true }) : null;
          console.log('Observer next, validation state: ', validationState); // Gets correct val state
          observer.next(validationState);
          console.log('Is it hitting this'); // Gets hit
        }, (err) => {
          observer.next(null);
        });
    });

Важное замечание: когда я раскомментирую // return observableOf({ 'isDuplicateEmail': true });, все работает, как ожидалось. Но как вы видите код выше, он не работает, даже если наблюдаемый возвращает правильное значение (зарегистрировал его как использованный отладчик). Под «не работает» я имею в виду, что элемент управления формы никогда не переходит в состояние ошибки, как показано:

<pre>IS INVALID: {{emailAddress.invalid}}</pre>
<pre>ERRORS: {{emailAddress.errors | json}}</pre>

Почему и как мне это исправить?


person VSO    schedule 20.08.2019    source источник


Ответы (3)


у вас 3 вопроса. 1. вам действительно нужно вернуть наблюдаемый объект 2. вы отправляете наблюдаемый объект вместо значения через своего наблюдателя 3. наблюдаемый объект должен завершиться.

вы могли бы сделать это:

  private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
    const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
      this.adminService.getUser(emailAddress).subscribe(
        (res: any) => {
          const validationState = !!res ? { 'isDuplicateEmail': true } : null; // just send the value
          observer.next(validationState);
          observer.complete(); //complete here
        }, (err) => {
          observer.next(null);
          observer.complete(); // and complete here
        });
    });
    return validationStateObs; // return it here
  }

но проще и чище было бы просто вернуть ответ от вашей службы и использовать операторы вместо создания наблюдаемого и вложить в него подписку.

  private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
    return this.adminService.getUser(emailAddress).pipe(
      map((res: any) => !!res ? { 'isDuplicateEmail': true } : null),
      catchError((err) => observableOf(null))
    )
  }

http observables естественно завершаются после одного выброса, поэтому вам не нужно беспокоиться о завершении или любой другой сложной логике, которая у вас была.

person bryan60    schedule 20.08.2019
comment
Это ответ, который я ищу! Другие также сталкиваются с этой проблемой - AsyncValidatorFn не показывает ошибки, одним из ключевых моментов является то, что ошибка не будет отображаться до тех пор, пока не завершится возвращенный Observable. - person Ellie Wu; 03.05.2021

Вопрос в том, почему вы подписываетесь, а затем возвращаете это как новое Observable? Думаю, у вас должно получиться так:

private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
    return this.adminService.getUser(emailAddress).pipe(map(
      (res: any) => {
        return !!res ? { 'isDuplicateEmail': true } : null;
      }, catchError(err => {
        return of(null);
      })));
  }

Таким образом, вы позволите самому валидатору async обрабатывать подписку за вас ...

Надеюсь это поможет...

person miselking    schedule 20.08.2019
comment
catchError ожидает наблюдаемого возвращаемого значения - person bryan60; 20.08.2019
comment
Конечно ... Я не пробовал это, просто написал в лоб ... Рад, что помог ... :) - person miselking; 20.08.2019

observableOf({ 'isDuplicateEmail': true }) возвращает Observable, верно? вы должны поместить обычный метод в Observer.next (), а не в наблюдаемый.

попробуйте удалить observableOf и протестировать его. должно получиться примерно так:

  private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
    const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
      this.adminService.getUser(emailAddress).subscribe(
        (res: any) => {
          const validationState = !!res ? { 'isDuplicateEmail': true } : null;
          console.log('Observer next, validation state: ', validationState); // Gets correct val state
          observer.next(validationState);
          console.log('Is it hitting this'); // Gets hit
        }, (err) => {
          observer.next(null);
        });
    });

Я надеюсь, что так работает

person Peyman    schedule 20.08.2019