自定义ExceptionHandler更改检测滞后

时间:2016-06-13 15:10:23

标签: angular typescript

我正在尝试在Angular 2应用中实现自定义ExceptionHandler,该应用会将未捕获的错误提交给自定义AlertsService。目标是允许主App组件订阅AlertsService提供的警报,以便它可以在UI中显示错误。

我遇到的问题是,在遇到其他错误之前,自定义AlertsService提交给ExceptionHandler的错误不会反映在用户界面中。这导致UI始终是AlertsService实际提供的内容之后的一个警报。

我的猜测是这种行为与变更检测和ExceptionHandler的特殊情况有关,但我不知道从哪里开始。期待Angular2专家的帮助!

下面的示例代码,plunk here

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 个答案:

答案 0 :(得分:8)

更新 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);
}

Plunker example