diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md
index eda05f4af2d..4db27e1222d 100644
--- a/aio/content/guide/deprecations.md
+++ b/aio/content/guide/deprecations.md
@@ -396,13 +396,12 @@ available in `RouterModule.forRoot` or `provideRouter` and `withRouterConfig`.
* `paramsInheritanceStrategy`
* `urlUpdateStrategy`
* `canceledNavigationResolution`
+* `errorHandler`
The following options are available in `RouterModule.forRoot` but not
available in `provideRouter`:
* `malformedUriErrorHandler` - This was not found to be used by anyone.
There are currently no plans to make this available in `provideRouter`.
-* `errorHandler` - Developers should instead subscribe to `Router.events`
- and filter for `NavigationError`.
diff --git a/goldens/public-api/router/index.md b/goldens/public-api/router/index.md
index 265c3388cd8..48304d17823 100644
--- a/goldens/public-api/router/index.md
+++ b/goldens/public-api/router/index.md
@@ -246,6 +246,9 @@ export type DisabledInitialNavigationFeature = RouterFeature;
+// @public
+export type ErrorHandlerFeature = RouterFeature;
+
// @public
type Event_2 = RouterEvent | NavigationStart | NavigationEnd | NavigationCancel | NavigationError | RoutesRecognized | GuardsCheckStart | GuardsCheckEnd | RouteConfigLoadStart | RouteConfigLoadEnd | ChildActivationStart | ChildActivationEnd | ActivationStart | ActivationEnd | Scroll | ResolveStart | ResolveEnd | NavigationSkipped;
export { Event_2 as Event }
@@ -748,7 +751,7 @@ export interface RouterFeature {
}
// @public
-export type RouterFeatures = PreloadingFeature | DebugTracingFeature | InitialNavigationFeature | InMemoryScrollingFeature | RouterConfigurationFeature;
+export type RouterFeatures = PreloadingFeature | DebugTracingFeature | InitialNavigationFeature | InMemoryScrollingFeature | RouterConfigurationFeature | ErrorHandlerFeature;
// @public
export type RouterHashLocationFeature = RouterFeature;
@@ -1071,6 +1074,9 @@ export function withDisabledInitialNavigation(): DisabledInitialNavigationFeatur
// @public
export function withEnabledBlockingInitialNavigation(): EnabledBlockingInitialNavigationFeature;
+// @public
+export function withErrorHandler(fn: (error: NavigationError) => void): ErrorHandlerFeature;
+
// @public
export function withHashLocation(): RouterConfigurationFeature;
diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts
index fd07f070df1..b7bf6815ddf 100644
--- a/packages/router/src/index.ts
+++ b/packages/router/src/index.ts
@@ -15,7 +15,7 @@ export {ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart
export {CanActivate, CanActivateChild, CanActivateChildFn, CanActivateFn, CanDeactivate, CanDeactivateFn, CanLoad, CanLoadFn, CanMatch, CanMatchFn, Data, DefaultExport, LoadChildren, LoadChildrenCallback, NavigationBehaviorOptions, OnSameUrlNavigation, QueryParamsHandling, Resolve, ResolveData, ResolveFn, Route, Routes, RunGuardsAndResolvers, UrlMatcher, UrlMatchResult} from './models';
export {Navigation, NavigationExtras, UrlCreationOptions} from './navigation_transition';
export {DefaultTitleStrategy, TitleStrategy} from './page_title_strategy';
-export {DebugTracingFeature, DisabledInitialNavigationFeature, EnabledBlockingInitialNavigationFeature, InitialNavigationFeature, InMemoryScrollingFeature, PreloadingFeature, provideRouter, provideRoutes, RouterConfigurationFeature, RouterFeature, RouterFeatures, RouterHashLocationFeature, withDebugTracing, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation, withHashLocation, withInMemoryScrolling, withPreloading, withRouterConfig} from './provide_router';
+export {DebugTracingFeature, DisabledInitialNavigationFeature, EnabledBlockingInitialNavigationFeature, InitialNavigationFeature, InMemoryScrollingFeature, NavigationErrorHandlerFeature as ErrorHandlerFeature, PreloadingFeature, provideRouter, provideRoutes, RouterConfigurationFeature, RouterFeature, RouterFeatures, RouterHashLocationFeature, withDebugTracing, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation, withHashLocation, withInMemoryScrolling, withNavigationErrorHandler as withErrorHandler, withPreloading, withRouterConfig} from './provide_router';
export {BaseRouteReuseStrategy, DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
export {Router} from './router';
export {ExtraOptions, InitialNavigation, InMemoryScrollingOptions, ROUTER_CONFIGURATION, RouterConfigOptions} from './router_config';
diff --git a/packages/router/src/provide_router.ts b/packages/router/src/provide_router.ts
index 9836cd17ca1..d98be8d8fc4 100644
--- a/packages/router/src/provide_router.ts
+++ b/packages/router/src/provide_router.ts
@@ -7,7 +7,7 @@
*/
import {HashLocationStrategy, LOCATION_INITIALIZED, LocationStrategy, ViewportScroller} from '@angular/common';
-import {APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, ApplicationRef, ComponentRef, ENVIRONMENT_INITIALIZER, EnvironmentProviders, inject, InjectFlags, InjectionToken, Injector, makeEnvironmentProviders, NgZone, Provider, Type} from '@angular/core';
+import {APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, ApplicationRef, ComponentRef, ENVIRONMENT_INITIALIZER, EnvironmentInjector, EnvironmentProviders, inject, InjectFlags, InjectionToken, Injector, makeEnvironmentProviders, NgZone, Provider, Type} from '@angular/core';
import {of, Subject} from 'rxjs';
import {filter, map, take} from 'rxjs/operators';
@@ -621,6 +621,63 @@ export function withHashLocation(): RouterConfigurationFeature {
return routerFeature(RouterFeatureKind.RouterConfigurationFeature, providers);
}
+/**
+ * A type alias for providers returned by `withNavigationErrorHandler` for use with `provideRouter`.
+ *
+ * @see `withNavigationErrorHandler`
+ * @see `provideRouter`
+ *
+ * @publicApi
+ */
+export type NavigationErrorHandlerFeature =
+ RouterFeature;
+
+/**
+ * Subscribes to the Router's navigation events and calls the given function when a
+ * `NavigationError` happens.
+ *
+ * This function is run inside application's injection context so you can use the `inject` function.
+ *
+ * @usageNotes
+ *
+ * Basic example of how you can use the error handler option:
+ * ```
+ * const appRoutes: Routes = [];
+ * bootstrapApplication(AppComponent,
+ * {
+ * providers: [
+ * provideRouter(appRoutes, withNavigationErrorHandler((e: NavigationError) =>
+ * inject(MyErrorTracker).trackError(e))
+ * ]
+ * }
+ * );
+ * ```
+ *
+ * @see `NavigationError`
+ * @see `inject`
+ * @see `EnvironmentInjector#runInContext`
+ *
+ * @returns A set of providers for use with `provideRouter`.
+ *
+ * @publicApi
+ */
+export function withNavigationErrorHandler(fn: (error: NavigationError) => void):
+ NavigationErrorHandlerFeature {
+ const providers = [{
+ provide: ENVIRONMENT_INITIALIZER,
+ multi: true,
+ useValue: () => {
+ const injector = inject(EnvironmentInjector);
+ inject(Router).events.subscribe((e) => {
+ if (e instanceof NavigationError) {
+ injector.runInContext(() => fn(e));
+ }
+ });
+ }
+ }];
+ return routerFeature(RouterFeatureKind.NavigationErrorHandlerFeature, providers);
+}
+
/**
* A type alias that represents all Router features available for use with `provideRouter`.
* Features can be enabled by adding special functions to the `provideRouter` call.
@@ -632,7 +689,7 @@ export function withHashLocation(): RouterConfigurationFeature {
* @publicApi
*/
export type RouterFeatures = PreloadingFeature|DebugTracingFeature|InitialNavigationFeature|
- InMemoryScrollingFeature|RouterConfigurationFeature;
+ InMemoryScrollingFeature|RouterConfigurationFeature|NavigationErrorHandlerFeature;
/**
* The list of features as an enum to uniquely type each feature.
@@ -644,5 +701,6 @@ export const enum RouterFeatureKind {
DisabledInitialNavigationFeature,
InMemoryScrollingFeature,
RouterConfigurationFeature,
- RouterHashLocationFeature
+ RouterHashLocationFeature,
+ NavigationErrorHandlerFeature,
}
diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts
index fd00d3a91b0..8236fa9f6cd 100644
--- a/packages/router/test/integration.spec.ts
+++ b/packages/router/test/integration.spec.ts
@@ -17,7 +17,7 @@ import {concat, EMPTY, Observable, Observer, of, Subscription} from 'rxjs';
import {delay, filter, first, last, map, mapTo, takeWhile, tap} from 'rxjs/operators';
import {CanActivateChildFn, CanActivateFn, CanMatch, CanMatchFn, Data, ResolveFn} from '../src/models';
-import {provideRouter, withRouterConfig} from '../src/provide_router';
+import {provideRouter, withNavigationErrorHandler, withRouterConfig} from '../src/provide_router';
import {forEach, wrapIntoObservable} from '../src/utils/collection';
import {getLoadedRoutes} from '../src/utils/config';
@@ -1668,6 +1668,29 @@ describe('Integration', () => {
]);
})));
+ it('should be able to provide an error handler with DI dependencies', async () => {
+ @Injectable({providedIn: 'root'})
+ class Handler {
+ handlerCalled = false;
+ }
+ TestBed.configureTestingModule({
+ providers: [
+ provideRouter(
+ [{
+ path: 'throw',
+ canMatch: [() => {
+ throw new Error('');
+ }],
+ component: BlankCmp
+ }],
+ withNavigationErrorHandler(() => coreInject(Handler).handlerCalled = true)),
+ ]
+ });
+ const router = TestBed.inject(Router);
+ await expectAsync(router.navigateByUrl('/throw')).toBeRejected();
+ expect(TestBed.inject(Handler).handlerCalled).toBeTrue();
+ });
+
// Errors should behave the same for both deferred and eager URL update strategies
['deferred', 'eager'].forEach((strat: any) => {
it('should dispatch NavigationError after the url has been reset back', fakeAsync(() => {