Angular 2: Задержка при обнаружении изменений в ExceptionHandler

Я пытаюсь реализовать кастомExceptionHandler в приложении Angular 2, которое отправляет необработанные ошибки в пользовательскийAlertsService, Цель состоит в том, чтобы основнойApp компонент для подписки на оповещения, предоставленныеAlertsService так что он может отображать ошибки в пользовательском интерфейсе.

Проблема, с которой я сталкиваюсь, заключается в том, чтоAlertsService по обычаюExceptionHandler не отображаются в пользовательском интерфейсе, пока не встретится другая ошибка. Это заставляет пользовательский интерфейс всегда быть одним предупреждением позади того, что фактически предоставляетсяAlertsService.

Я предполагаю, что это поведение как-то связано с обнаружением изменений и особым случаем ExceptionHandler, но я не уверен, куда идти дальше. Глядя на помощь экспертов Angular2!

Пример кода ниже, вставьте сюда:https://plnkr.co/edit/xPoWCELA9IHqeLBS4wXs?p=preview

import { Component, ExceptionHandler, Injectable, OnInit, provide } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';
import { Subject } from 'rxjs/Subject'

export interface Alert {
  message: string;
}

@Injectable()
export class AlertsService {

  private alertTriggeredSubject = new Subject<Alert>();

  alertTriggered = this.alertTriggeredSubject.asObservable();

  triggerAlert(message: string) {
    this.alertTriggeredSubject.next(<Alert>{ message: message });
  }

}

@Injectable()
export class CustomExceptionHander {

  constructor(private alertsService: AlertsService) { }

  call(exception, stackTrace = null, reason = null) {
    this.alertsService.triggerAlert(exception.originalException);
    console.error('EXCEPTION:', exception);
  }
}

@Component({
  selector: 'child-component',
  template : `
  <h3>Child</h3>
  <div id="child">
    <button (click)="breakMe()">Break Me!</button>
    <div>Alerts Sent:</div>
    <ul><li *ngFor="let error of errors">{{error}}</li></ul>
  </div>`
})
export class ChildComponent {

  errors: string[] = [];
  numErrors = 0

  breakMe() {
    this.numErrors++;
    let error = `I broke it (${this.numErrors})`;

    // The error added to the array below is never reflected in the 
    // "Alerts Sent:" <ul>...not sure why
    this.errors.push(error);
    console.info('ChildComponent.errors', this.errors);

    // Simulate unhandled exception
    throw new Error(error);
  }
}

@Component({
  selector: 'my-app',
  template : `
  <h3>Parent</h3>
  <div id="parent">
    <div>Alerts Received:</div>
    <ul><li *ngFor="let alert of alerts">{{alert.message}}</li></ul>
    <child-component></child-component>
  </div>`
  directives: [ChildComponent]
})
export class App implements OnInit {

  constructor(private alertsService: AlertsService) { }

  alerts: Alert[] = [];

  ngOnInit() {
    this.alertsService.alertTriggered.subscribe(alert => {
      this.alerts.push(alert);

      // Alert gets received, but is not reflected in the UI
      // until the next alert is received, even thought the 
      // alerts[] is up-to-date.
      console.info('App alert received:', alert);
      console.info('App.alerts:', this.alerts);
    });
  }
}

bootstrap(App, [
    AlertsService,
    provide(ExceptionHandler, { useClass: CustomExceptionHander })
]).catch(err => console.error(err));

Ответы на вопрос(1)

Решение Вопроса

Обновить ExceptionHandler был переименован вErrorHandler https://stackoverflow.com/a/35239028/217408

orgiginal

Обнаружение изменений не выполняется в концеclick событие, когда обработчик бросает.

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

Обходной путь должен вместоApplicationRef ввестиInjector и приобрестиAplicationRef обязательно

constructor(private alertsService: AlertsService, injector:Injector) { 
  setTimeout(() => this.appRef = injector.get(ApplicationRef));
}

а затем вcall вызвать обнаружение изменений как

call(exception, stackTrace = null, reason = null) {
  this.alertsService.triggerAlert(exception.originalException);
  this.appRef.tick();
  console.error('EXCEPTION:', exception);
}

Пример плунжера

 Günter Zöchbauer13 июн. 2016 г., 19:46
Я пропустил эту часть. Я не смог найти способ обновить этот компонент. Это может стоить сообщения об ошибке. Я не знаю, каково ожидаемое поведение здесь.
 davidenke09 нояб. 2017 г., 15:24
@ GünterZöchbauer Знаете ли вы, как этого добиться, используя Angular 5? Кажется, чтоApplicationRef стал интерфейсом иInjector терпит неудачу с чем-то вродеUncaught Error: StaticInjectorError[ApplicationRef]: NullInjectorError: No provider for ApplicationRef!...
 Günter Zöchbauer09 нояб. 2017 г., 15:28
Я не видел ничего связанного в журнале изменений, и в источнике это все еще классgithub.com/angular/angular/blob/5.0.x/packages/core/src/...
 WayneC13 июн. 2016 г., 19:24
Спасибо за быстрый ответ. Итак, если я правильно понимаю, вы обновили обработчик исключений, чтобы вручную инициировать обнаружение изменений для обработки случаев, когда обнаружение изменений не было инициировано из-за исключения? Не совсем интуитивно, но это работает.
 davidenke09 нояб. 2017 г., 15:33
Да, я был смущен, потому что документы изменились ...
 Günter Zöchbauer15 июн. 2016 г., 15:14
Недавно я увидел комментарий к проблеме, где говорится, что исключения должны обрабатываться локально там, где они появляются.ExceptionHandler только для необработанных исключений для центральной регистрации ошибок. Вы не должны полагаться на то, что ваше приложение все еще работает должным образом послеExceptionHandler был вызван.
 WayneC13 июн. 2016 г., 19:27
Один ответ на вопрос, прежде чем я отмечу это как принятое. Кроме того, в обработчике нажатия кнопки ошибка передается в ошибки ChildComponent []. Это значение никогда не обновляется в пользовательском интерфейсе, хотя мы теперь вручную запускаем обнаружение изменений в ExceptionHandler. Есть идеи почему?
 Günter Zöchbauer13 июн. 2016 г., 19:26
Я предполагаю, что безопасно делать дополнительный цикл обнаружения изменений после исключения. Это не должно быть слишком часто ;-) Может быть, есть лучший способ. Я не эксперт в реализации пользовательских обработчиков исключений.
 Igor Nadj31 янв. 2018 г., 05:41
Пример в плунжере показывает один тик обнаружения изменений, он не делает больше одного. Посмотрите на этот пример разветвленного плунжера, где изменения пользовательского интерфейса не происходят после первого исключения (нажмите do do it after break me):plunker
 Günter Zöchbauer31 янв. 2018 г., 05:47
Вы не должны предполагать, что приложение находится в рабочем состоянии после того, как обработчик exceptiin был вызван. Вы можете отправить информацию на сервер об исключении, вы можете сообщить пользователю о сбое приложения, его необходимо перезагрузить, а затем перезагрузить. Обработчик исключений не должен поддерживать работу приложения.

Ваш ответ на вопрос