I have an Angular app with a custom ErrorHandler. My goal is to allow components (say, the AppComponent) to be notified when a global error occurs so that they can present it in the interface. I implemented this by extending ErrorHandler (as described in the documentation), and I added a Subject property called onError$ which emits a value when handleError() is called. I then inject ErrorHandlerService into the app component, and it subscribes to onError$. Here's a minimal repro (also a Stackblitz here):
Repro
error-handler.service.ts
import { ErrorHandler, Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class ErrorHandlerService implements ErrorHandler {
onError$ = new Subject<string>();
handleError(error: any): void {
this.onError$.next(`An error: ${error}`);
console.log("Hi from ErrorHandlerService! I called next().");
}
}
app.module.ts
import { ErrorHandler, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ErrorHandlerService } from './error-handler.service';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [{ provide: ErrorHandler, useClass: ErrorHandlerService }],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component, OnInit } from '@angular/core';
import { ErrorHandlerService } from './error-handler.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
})
export class AppComponent implements OnInit {
title = "SO Angular Error Question";
constructor(private errorService: ErrorHandlerService) { }
ngOnInit(): void {
this.errorService.onError$.asObservable().subscribe(error => {
console.log("Hi from the subscription to the onError$ subject!", error);
});
throw new Error('Method not implemented.');
}
}
Expected result
When I manually throw the error in app.component.ts, I expect the console.log statement in ErrorHandlerService to run, and then I expect the subscription callback in AppComponent to run its console.log, resulting in two messages being printed to the console from my app.
Observed result
The console.log from ErrorHandlerService happens, but the one in AppComponent's subscription callback doesn't.
My question
Why does the callback for the subscription in AppComponent not occur?
This question seems related, but it doesn't have an accepted answer, and adding .asObservable() before subscribing in the component doesn't resolve the problem. This question is also related and "solves" the problem by calling a method on an error reporting service explicitly in the error handler, but I'm still curious why my solution doesn't work.
My suspicion is that despite the fact that my ErrorHandlerService is marked with providedIn: 'root', the framework creates an instance to act as the global error handler, and the AppComponent is receiving a second one, but I can't find documentation about this behavior anywhere. Any help appreciated!
