Issue
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!
Solution
Cool question, nicely formatted
tl;dr
//in app.component.ts
constructor(@Inject(ErrorHandler) private errorService: ErrorsService,
This is a (uncommon) dependancy injection problem
In your app.module.ts you have
providers: [{ provide: ErrorHandler, useClass: ErrorsService }],
The token is ErrorHandler, the provided thing is ErrorsService
When you inject a service in your constructor like
constructor(private errorService: ErrorsService
That's shorthand for
constructor(@Inject(ErrorsService) private errorService: ErrorsService
Here the token is ErrorsService, the provided thing is ErrorsService
Angular looks for providers of tokens to fulfill the DI, so the fix is to use the right token.
Answered By - Donald Duck

0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.