Angular 6: Наблюдаемая асинхронная привязка не работает должным образом после HttpErrorResponse - PullRequest
0 голосов
/ 17 октября 2018

Я пытаюсь обрабатывать ошибки в угловых значениях глобально, используя ErrorHandler, как изложено здесь: https://medium.com/@aleixsuau/error-handling-angular-859d529fa53a

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

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

При HttpErrorResponses поведение выглядит странно: ошибка перехватывается, уведомление отправляется, но пользовательский интерфейс не обновляется.Если только другой HttpErrorResponse не будет выдан в течение 3 секунд!

Я что-то упустил или это ошибка в Angular 6 или RxJs?

Я создал минимальный, полный и проверяемый пример on stackblitz: https://stackblitz.com/edit/angular-e9keuw

ErrorHandler:

@Injectable()
export class ErrorSink implements ErrorHandler {

    // ErrorHandler is created before the providers
    // we have to use the Injector to get them
    constructor(private injector: Injector) {}

    handleError(error: Error | HttpErrorResponse) {
        console.error('Caught error: ', error);

        const notificationService = this.injector.get(NotificationService);

        // client error
        if (!(error instanceof HttpErrorResponse)) {
            console.log('client error!');
            return notificationService.notify(error.message);
        }

        // offline error
        if (!navigator.onLine) {
            console.log('No Internet Connection');
            return notificationService.notify('No Internet Connection');
        }

        // http error
        console.log(error.status, error.message);
        return notificationService.notify(`${error.status} - ${error.message}`);
    }
}

Служба уведомлений:

@Injectable()
export class NotificationService {

  private subject: BehaviorSubject<string> = new BehaviorSubject(null);
  readonly notification$: Observable<string> = this.subject.asObservable();

  constructor() {}

  notify(message) {
    console.log('notification', message)

    this.subject.next(message);
    setTimeout(() => this.subject.next(null), 3000);
  }
}

Компонент:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  constructor(
    private notificationService: NotificationService,
    private http: HttpClient
  ) {}

  throwError(): void {
    throw new Error('an error was thrown!');
  }

  loadUrl() {
    this.http.get('https://www.google.com/').subscribe(
      data => console.log(data)
    );
  }
}

Асвязанный шаблон:

<div *ngIf="notificationService.notification$ | async as notification">
  {{ notification }}
</div>

1 Ответ

0 голосов
/ 17 октября 2018

Причина в том, что ошибка вызывается за пределами зоны.Я не знаю точную причину, почему это происходит, потому что я не вижу весь ваш код, но это так :).Обновите службу NotificationService, чтобы запускать уведомление внутри зоны:

@Injectable()
export class NotificationService {

  private subject: BehaviorSubject<string> = new BehaviorSubject(null);
  readonly notification$: Observable<string> = this.subject.asObservable();

  private timeout: number = 0;

  constructor(readonly zone: NgZone) {}

  notify(message) {
    console.log('notification', message)

    clearTimeout(this.timeout);

    this.zone.run(() => {
      this.subject.next(message);
      this.timeout = setTimeout(() => this.subject.next(null), 3000);
    });
  }
}

Один совет: сохраните ссылку setTimeout в члене класса.Таким образом, вы можете отменить setTimeout, если у вас есть две ошибки в течение 3 секунд.Может случиться так, что вторая ошибка не может быть прочитана, потому что она уже установлена ​​в нуль

...