feat(router): add support for custom error handlers

This commit is contained in:
vsavkin 2016-08-25 07:56:30 -07:00 committed by Victor Berchet
parent 93f323cfa2
commit 2fc5c57b31
4 changed files with 53 additions and 2 deletions

View File

@ -201,6 +201,21 @@ export class RoutesRecognized {
export type Event = export type Event =
NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized; NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized;
/**
* Error handler that is invoked when a navigation errors.
*
* If the handler retuns a value, the navigation promise will be resolved with this value.
* If the handler throws an exception, the navigation promise will be rejected with
* the exception.
*
* @stable
*/
export type ErrorHandler = (error: any) => any;
function defaultErrorHandler(error: any): any {
throw error;
}
/** /**
* The `Router` is responsible for mapping URLs to components. * The `Router` is responsible for mapping URLs to components.
* *
@ -216,6 +231,8 @@ export class Router {
private navigationId: number = 0; private navigationId: number = 0;
private configLoader: RouterConfigLoader; private configLoader: RouterConfigLoader;
errorHandler: ErrorHandler = defaultErrorHandler;
/** /**
* Indicates if at least one navigation happened. * Indicates if at least one navigation happened.
* *
@ -533,7 +550,11 @@ export class Router {
resolvePromise(false); resolvePromise(false);
} else { } else {
this.routerEvents.next(new NavigationError(id, this.serializeUrl(url), e)); this.routerEvents.next(new NavigationError(id, this.serializeUrl(url), e));
rejectPromise(e); try {
resolvePromise(this.errorHandler(e));
} catch (ee) {
rejectPromise(ee);
}
} }
this.currentRouterState = storedState; this.currentRouterState = storedState;
this.currentUrlTree = storedUrl; this.currentUrlTree = storedUrl;

View File

@ -13,7 +13,7 @@ import {Route, Routes} from './config';
import {RouterLink, RouterLinkWithHref} from './directives/router_link'; import {RouterLink, RouterLinkWithHref} from './directives/router_link';
import {RouterLinkActive} from './directives/router_link_active'; import {RouterLinkActive} from './directives/router_link_active';
import {RouterOutlet} from './directives/router_outlet'; import {RouterOutlet} from './directives/router_outlet';
import {Router} from './router'; import {ErrorHandler, Router} from './router';
import {ROUTES} from './router_config_loader'; import {ROUTES} from './router_config_loader';
import {RouterOutletMap} from './router_outlet_map'; import {RouterOutletMap} from './router_outlet_map';
import {ActivatedRoute} from './router_state'; import {ActivatedRoute} from './router_state';
@ -137,11 +137,18 @@ export function provideRoutes(routes: Routes): any {
/** /**
* Extra options used to configure the router.
*
* Set `enableTracing` to log router events to the console.
* Set 'useHash' to true to enable HashLocationStrategy.
* Set `errorHandler` to enable a custom ErrorHandler.
*
* @stable * @stable
*/ */
export interface ExtraOptions { export interface ExtraOptions {
enableTracing?: boolean; enableTracing?: boolean;
useHash?: boolean; useHash?: boolean;
errorHandler?: ErrorHandler;
} }
export function setupRouter( export function setupRouter(
@ -156,6 +163,10 @@ export function setupRouter(
componentType, urlSerializer, outletMap, location, injector, loader, compiler, componentType, urlSerializer, outletMap, location, injector, loader, compiler,
flatten(config)); flatten(config));
if (opts.errorHandler) {
r.errorHandler = opts.errorHandler;
}
if (opts.enableTracing) { if (opts.enableTracing) {
r.events.subscribe(e => { r.events.subscribe(e => {
console.group(`Router Event: ${(<any>e.constructor).name}`); console.group(`Router Event: ${(<any>e.constructor).name}`);

View File

@ -427,6 +427,23 @@ describe('Integration', () => {
]); ]);
}))); })));
it('should support custom error handlers', fakeAsync(inject([Router], (router: Router) => {
router.errorHandler = (error) => 'resolvedValue';
const fixture = createRoot(router, RootCmp);
router.resetConfig([{path: 'user/:name', component: UserCmp}]);
const recordedEvents: any[] = [];
router.events.forEach(e => recordedEvents.push(e));
let e: any;
router.navigateByUrl('/invalid').then(_ => e = _);
advance(fixture);
expect(e).toEqual('resolvedValue');
expectEvents(recordedEvents, [[NavigationStart, '/invalid'], [NavigationError, '/invalid']]);
})));
it('should replace state when path is equal to current path', it('should replace state when path is equal to current path',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);

View File

@ -72,6 +72,7 @@ export declare type Event = NavigationStart | NavigationEnd | NavigationCancel |
/** @stable */ /** @stable */
export interface ExtraOptions { export interface ExtraOptions {
enableTracing?: boolean; enableTracing?: boolean;
errorHandler?: ErrorHandler;
useHash?: boolean; useHash?: boolean;
} }
@ -168,6 +169,7 @@ export interface Route {
/** @stable */ /** @stable */
export declare class Router { export declare class Router {
config: Routes; config: Routes;
errorHandler: ErrorHandler;
events: Observable<Event>; events: Observable<Event>;
/** @experimental */ navigated: boolean; /** @experimental */ navigated: boolean;
routerState: RouterState; routerState: RouterState;