16

While working inside Angular (Angular 4, 5), if a component raises Error (TypeError or null or undefined error or so), whole application breaks onward.

How can we deal with this, to catch errors on component level and possibly show a fallback UI, like React16 does using Error Boundaries.

R. Richards
  • 24,603
  • 10
  • 64
  • 64
abdul-wahab
  • 2,182
  • 3
  • 21
  • 35
  • Currently there is support to override the *global* implementation of the `ErrorHandler`. You could try to instantiate a provider object that overrides the default implementation at component level using the providers property of the Component decorator. – Jota.Toledo Jan 22 '18 at 11:45
  • I am already overriding *global* ErrorHandler. If it can be provided at component level, it'd be great. Are you sure it's possible? – abdul-wahab Jan 22 '18 at 11:47
  • Probably too broad for what you'd like to have, but [overriding](https://stackoverflow.com/a/40007743/8555393) the [ExceptionHandler](https://angular.io/api/core/ErrorHandler) by your own is a possibility. See [this article](https://www.loggly.com/blog/angular-exception-logging-made-simple/) for more detailed usage. – Tomas Varga Jan 22 '18 at 11:49
  • Doesnt seem to be possible atm http://plnkr.co/edit/oZPbmFoqURuQQLqvpjKw?p=info – Jota.Toledo Jan 22 '18 at 13:55

2 Answers2

1

I would approach it by handling the error at Component level and have a service that listens to any errors happening at Component or Service level. Ex:

  1. Throw the error from the service
  2. catch the error in component
  3. Handle the error, process it and send the Error event with details to ErrorService.
  4. You can have a app level component "errorBannerComponent" which takes input from ErrorService and paint your UI.
  5. As soon as the error is received in ErrorService, The errorBannerComponent should display the error on screen.

Hope it helps.

Also By default, Angular comes with its own ErrorHandler that intercepts all the Errors that happen in our app and logs them to the console, preventing the app from crashing. We can modify this default behavior by creating a new class that implements the ErrorHandler:

You can find more details and example here:

nircraft
  • 8,242
  • 5
  • 30
  • 46
  • If say `ngAfterContentChecked` life cycle method raises _undefined_ error, how can it be catched inside component or service. – abdul-wahab Nov 06 '18 at 20:59
  • there must be something unexpected going on in your code there. You should inspect your code. These lifeCycle hooks are comparatively safe to use compared to other functions. – nircraft Nov 06 '18 at 21:06
  • You can share your code at stackblitz and i can take a look. – nircraft Nov 06 '18 at 21:07
  • 2
    React's Error Boundaries ensure that if there's an unprecedented error at run time, it won't crash the application, also user will see a fall back UI. Run time errors, that aren't evident at compile/develop time, will crash Angular application. I was looking to solve this problem. – abdul-wahab Nov 07 '18 at 06:36
  • 2
    By default, Angular comes with its own ErrorHandler that intercepts all the Errors that happen in our app and logs them to the console, preventing the app from crashing. This can be modified by creating a new class that implements the ErrorHandler: You can read more here: https://medium.com/@aleixsuau/error-handling-angular-859d529fa53a – nircraft Nov 07 '18 at 14:35
1

As the proposed solutions are rather dull. I tried to recreate it myself. The easiest solution would be to provide a module scoped custom ErrorHandler class. Thanks to this, you could even create a multiple different ErrorBoundaries.

My proposed solution can be seen here: https://stackblitz.com/edit/angular-ivy-brb143?file=src/app/widget/widget.module.ts

What is really important for this solution to work (atleast it didn't work otherwise for me). Was to provide the custom error handler as a part of a module rather than a component directly.

The important bits from the solutions:

module:

/**
 * This is really imporant as this allows us to provide a module scoped ErrorHandler
 */
@NgModule({
  imports: [CommonModule],
  declarations: [WidgetComponent],
  providers: [{ provide: ErrorHandler, useClass: WidgetErrorHandler }],
  exports: [WidgetComponent],
})
export class WidgetModule {}

component where we can throw, and catch error

@Component({
  selector: 'app-widget',
  templateUrl: './widget.component.html',
  styleUrls: ['./widget.component.css'],
})
export class WidgetComponent implements OnInit {
  constructor(@Inject(ErrorHandler) public widgetError: WidgetErrorHandler) {}

  ngOnInit() {
    this.widgetError.isError$.subscribe((error) =>
      console.log('component can act on error: ', error)
    );
  }

  public handleThrowErrorClick(): void {
    throw Error('Button clicked');
  }
}

and the handler iself


@Injectable()
export class WidgetErrorHandler implements ErrorHandler {
  public isError$: Subject<Error | any> = new Subject();

  handleError(error) {
    console.log('Intercepted error', error);
    this.isError$.next(error);
  }
}
karoluS
  • 2,980
  • 2
  • 23
  • 44
  • This is great and neat @karoluS. I was suspicious if it would catch template originated runtime errors (renderer level errors), I tested and it did catch. My test is [here](https://stackblitz.com/edit/angular-ivy-ahwwaf?file=src/app/widget/widget.component.html). I'll also do a few more tests. – abdul-wahab Apr 19 '22 at 23:48