diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index d1d944380e..d1e36a57ec 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -241,6 +241,14 @@ export class Router { routeReuseStrategy: RouteReuseStrategy = new DefaultRouteReuseStrategy(); + /** + * Define what the router should do if it receives a navigation request to the current URL. + * By default, the router will ignore this navigation. However, this prevents features such + * as a "refresh" button. Use this option to configure the behavior when navigating to the + * current URL. Default is 'ignore'. + */ + onSameUrlNavigation: 'reload'|'ignore' = 'ignore'; + /** * Creates the router service. */ @@ -552,7 +560,8 @@ export class Router { const url = this.urlHandlingStrategy.extract(rawUrl); const urlTransition = !this.navigated || url.toString() !== this.currentUrlTree.toString(); - if (this.urlHandlingStrategy.shouldProcessUrl(rawUrl)) { + if ((this.onSameUrlNavigation === 'reload' ? true : urlTransition) && + this.urlHandlingStrategy.shouldProcessUrl(rawUrl)) { (this.events as Subject).next(new NavigationStart(id, this.serializeUrl(url))); Promise.resolve() .then( diff --git a/packages/router/src/router_module.ts b/packages/router/src/router_module.ts index 8ecec6aff7..91d038c517 100644 --- a/packages/router/src/router_module.ts +++ b/packages/router/src/router_module.ts @@ -129,12 +129,15 @@ export class RouterModule { * Creates a module with all the router providers and directives. It also optionally sets up an * application listener to perform an initial navigation. * - * Options: + * Options (see {@link ExtraOptions}): * * `enableTracing` makes the router log all its internal events to the console. * * `useHash` enables the location strategy that uses the URL fragment instead of the history * API. * * `initialNavigation` disables the initial navigation. * * `errorHandler` provides a custom error handler. + * * `preloadingStrategy` configures a preloading strategy (see {@link PreloadAllModules}). + * * `onSameUrlNavigation` configures how the router handles navigation to the current URL. See + * {@link ExtraOptions} for more details. */ static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { return { @@ -267,6 +270,14 @@ export interface ExtraOptions { * Configures a preloading strategy. See {@link PreloadAllModules}. */ preloadingStrategy?: any; + + /** + * Define what the router should do if it receives a navigation request to the current URL. + * By default, the router will ignore this navigation. However, this prevents features such + * as a "refresh" button. Use this option to configure the behavior when navigating to the + * current URL. Default is 'ignore'. + */ + onSameUrlNavigation?: 'reload'|'ignore'; } export function setupRouter( @@ -299,6 +310,10 @@ export function setupRouter( }); } + if (opts.onSameUrlNavigation) { + router.onSameUrlNavigation = opts.onSameUrlNavigation; + } + return router; } diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 837d6f7f18..96e16fe98c 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -53,6 +53,7 @@ describe('Integration', () => { describe('navigation', function() { it('should navigate to the current URL', fakeAsync(inject([Router], (router: Router) => { + router.onSameUrlNavigation = 'reload'; router.resetConfig([ {path: '', component: SimpleCmp}, {path: 'simple', component: SimpleCmp}, @@ -3568,30 +3569,34 @@ describe('Integration', () => { }]); const events: any[] = []; - router.events.subscribe(e => onlyNavigationStartAndEnd(e) && events.push(e)); + router.events.subscribe(e => e instanceof RouterEvent && events.push(e)); location.go('/include/user/kate(aux:excluded)'); advance(fixture); expect(location.path()).toEqual('/include/user/kate(aux:excluded)'); - expectEvents( - events, - [[NavigationStart, '/include/user/kate'], [NavigationEnd, '/include/user/kate']]); + expectEvents(events, [ + [NavigationStart, '/include/user/kate'], [RoutesRecognized, '/include/user/kate'], + [GuardsCheckStart, '/include/user/kate'], [GuardsCheckEnd, '/include/user/kate'], + [ResolveStart, '/include/user/kate'], [ResolveEnd, '/include/user/kate'], + [NavigationEnd, '/include/user/kate'] + ]); events.splice(0); location.go('/include/user/kate(aux:excluded2)'); advance(fixture); - expectEvents( - events, - [[NavigationStart, '/include/user/kate'], [NavigationEnd, '/include/user/kate']]); - events.splice(0); + expectEvents(events, []); router.navigateByUrl('/include/simple'); advance(fixture); expect(location.path()).toEqual('/include/simple(aux:excluded2)'); - expectEvents( - events, [[NavigationStart, '/include/simple'], [NavigationEnd, '/include/simple']]); + expectEvents(events, [ + [NavigationStart, '/include/simple'], [RoutesRecognized, '/include/simple'], + [GuardsCheckStart, '/include/simple'], [GuardsCheckEnd, '/include/simple'], + [ResolveStart, '/include/simple'], [ResolveEnd, '/include/simple'], + [NavigationEnd, '/include/simple'] + ]); }))); }); }); diff --git a/tools/public_api_guard/router/router.d.ts b/tools/public_api_guard/router/router.d.ts index 0ade5f37ae..a1d5d899c8 100644 --- a/tools/public_api_guard/router/router.d.ts +++ b/tools/public_api_guard/router/router.d.ts @@ -126,6 +126,7 @@ export interface ExtraOptions { enableTracing?: boolean; errorHandler?: ErrorHandler; initialNavigation?: InitialNavigation; + onSameUrlNavigation?: 'reload' | 'ignore'; preloadingStrategy?: any; useHash?: boolean; } @@ -327,6 +328,7 @@ export declare class Router { errorHandler: ErrorHandler; readonly events: Observable; navigated: boolean; + onSameUrlNavigation: 'reload' | 'ignore'; routeReuseStrategy: RouteReuseStrategy; readonly routerState: RouterState; readonly url: string;