Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 + zonejs: routing stops working after uncaught error

If there is an uncaught error from a component (constructor or ngOnInit) during routing the navigation won't work anymore afterwards.

This happens even if there is an global ErrorHandler and an ErrorHandler for the RouterModule, added an ZoneListener to be sure as well - see app.module.ts

Minimal example here: https://embed.plnkr.co/L19S3hKWyqgKUIT1EJlI/preview

Make sure to open the console. Once you clicked "Example Component" there are some stacktraces as a result of the forced error in ExampleFormComponent. After that you can't navigate back to "Home".

How to handle unexpected, uncaught errors to make sure they don't break down the whole app?

like image 395
Benjamin Seiller Avatar asked May 09 '17 15:05

Benjamin Seiller


1 Answers

I would do some workaround like:

let hasRouterError = false;
@Injectable()
export class MyErrorHandler implements ErrorHandler {
  constructor(private inj: Injector) {}

  handleError(error: any): void {
    console.log('MyErrorHandler: ' + error);

    if(hasRouterError) {
      let router = this.inj.get(Router);
      router.navigated = false;
    }

    //throw error; // it is necessarily otherwise handleError won't be executed during next error
  }
}

export function MyRouterErrorHandler(error: any) {
  console.log('RouterErrorHandler: ' + error);
  hasRouterError = true;
  throw error;
}

and we have to use custom RouteReuseStrategy:

export class PreventErrorRouteReuseStrategy implements RouteReuseStrategy {
  shouldDetach(route: ActivatedRouteSnapshot): boolean { return false; }
  store(route: ActivatedRouteSnapshot, detachedTree: DetachedRouteHandle): void {}
  shouldAttach(route: ActivatedRouteSnapshot): boolean { return false; }
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null { return null; }
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    if(hasRouterError) {
      hasRouterError = false;
      return false;
    }
    return future.routeConfig === curr.routeConfig;
  }
}

It differs from the DefaultRouteReuseStrategy only one this code

if(hasRouterError) {
  hasRouterError = false;
  return false;
}

don't forget to add it to providers array:

import { RouteReuseStrategy } from '@angular/router';
...
{ provide: RouteReuseStrategy, useClass: PreventErrorRouteReuseStrategy },

You can try it in Modified Plunker

like image 68
yurzui Avatar answered Oct 24 '22 10:10

yurzui