Angular 6:可观察的异步绑定在 HttpErrorResponse 之后没有按预期工作

Mar*_*sch 0 error-handling rxjs angular

我正在尝试使用此处列出的 ErrorHandler 全局处理角度错误:https : //medium.com/@aleixsuau/error-handling-angular-859d529fa53a

我正在将错误消息转发给通知服务。应用组件模板绑定到服务使用异步管道提供的 observable。

当客户端错误被抛出时,一切都按预期工作:错误被捕获,通知被发送,UI 显示错误消息。3 秒后消息消失,因为 observable 变为空值。

在 HttpErrorResponses 上,行为很奇怪:错误被捕获,通知被发送,但 UI 没有更新。除非在 3 秒内抛出另一个 HttpErrorResponse!

我是否遗漏了什么,或者这是 Angular 6 或 RxJs 中的错误?

我在 stackblitz 上创建了一个最小、完整且可验证的示例https ://stackblitz.com/edit/angular-e9keuw

错误处理程序:

@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}`);
    }
}
Run Code Online (Sandbox Code Playgroud)

通知服务:

@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);
  }
}
Run Code Online (Sandbox Code Playgroud)

组件:

@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)
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

和绑定模板:

<div *ngIf="notificationService.notification$ | async as notification">
  {{ notification }}
</div>
Run Code Online (Sandbox Code Playgroud)

Pie*_*Duc 5

原因是错误是在区域外触发的。我不知道发生这种情况的确切原因,因为我没有看到您的所有代码,但它是 :)。更新您的 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);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

一个提示,将您的 setTimeout ref 保存在类成员中。这样,如果您在 3 秒内有两个错误,您可以取消 setTimeout。可能会发生无法读取第二个错误,因为它已设置为 null