2016-07-06 18:36:50 -04:00
|
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
|
*/
|
|
|
|
|
|
2018-05-17 07:33:50 -04:00
|
|
|
|
import {APP_BASE_HREF, HashLocationStrategy, LOCATION_INITIALIZED, Location, LocationStrategy, PathLocationStrategy, PlatformLocation, ViewportScroller} from '@angular/common';
|
2017-03-07 17:27:20 -05:00
|
|
|
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, APP_BOOTSTRAP_LISTENER, APP_INITIALIZER, ApplicationRef, Compiler, ComponentRef, Inject, Injectable, InjectionToken, Injector, ModuleWithProviders, NgModule, NgModuleFactoryLoader, NgProbeToken, Optional, Provider, SkipSelf, SystemJsNgModuleLoader} from '@angular/core';
|
2017-02-17 15:55:55 -05:00
|
|
|
|
import {ɵgetDOM as getDOM} from '@angular/platform-browser';
|
2018-02-27 17:06:06 -05:00
|
|
|
|
import {Subject, of } from 'rxjs';
|
2016-11-30 02:21:41 -05:00
|
|
|
|
|
2018-04-06 18:56:36 -04:00
|
|
|
|
import {EmptyOutletComponent} from './components/empty_outlet';
|
2016-08-16 16:40:28 -04:00
|
|
|
|
import {Route, Routes} from './config';
|
2016-07-06 19:19:52 -04:00
|
|
|
|
import {RouterLink, RouterLinkWithHref} from './directives/router_link';
|
|
|
|
|
import {RouterLinkActive} from './directives/router_link_active';
|
|
|
|
|
import {RouterOutlet} from './directives/router_outlet';
|
2018-02-27 17:06:06 -05:00
|
|
|
|
import {RouterEvent} from './events';
|
2016-11-30 02:21:41 -05:00
|
|
|
|
import {RouteReuseStrategy} from './route_reuse_strategy';
|
2016-08-25 10:56:30 -04:00
|
|
|
|
import {ErrorHandler, Router} from './router';
|
2016-07-06 19:19:52 -04:00
|
|
|
|
import {ROUTES} from './router_config_loader';
|
2017-05-17 20:47:34 -04:00
|
|
|
|
import {ChildrenOutletContexts} from './router_outlet_context';
|
2016-09-16 18:08:15 -04:00
|
|
|
|
import {NoPreloading, PreloadAllModules, PreloadingStrategy, RouterPreloader} from './router_preloader';
|
2018-05-17 07:33:50 -04:00
|
|
|
|
import {RouterScroller} from './router_scroller';
|
2017-03-15 17:32:03 -04:00
|
|
|
|
import {ActivatedRoute} from './router_state';
|
2016-10-20 13:44:44 -04:00
|
|
|
|
import {UrlHandlingStrategy} from './url_handling_strategy';
|
2018-04-10 06:01:07 -04:00
|
|
|
|
import {DefaultUrlSerializer, UrlSerializer, UrlTree} from './url_tree';
|
2016-08-16 16:40:28 -04:00
|
|
|
|
import {flatten} from './utils/collection';
|
2016-07-06 18:36:50 -04:00
|
|
|
|
|
|
|
|
|
|
2018-02-27 17:06:06 -05:00
|
|
|
|
|
2016-07-06 18:36:50 -04:00
|
|
|
|
/**
|
2018-04-05 06:51:21 -04:00
|
|
|
|
* @description
|
|
|
|
|
*
|
|
|
|
|
* Contains a list of directives
|
|
|
|
|
*
|
2018-04-05 17:31:44 -04:00
|
|
|
|
*
|
2016-07-06 18:36:50 -04:00
|
|
|
|
*/
|
2018-04-06 18:56:36 -04:00
|
|
|
|
const ROUTER_DIRECTIVES =
|
|
|
|
|
[RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, EmptyOutletComponent];
|
2016-07-06 18:36:50 -04:00
|
|
|
|
|
2016-08-16 16:40:28 -04:00
|
|
|
|
/**
|
2018-04-05 06:51:21 -04:00
|
|
|
|
* @description
|
|
|
|
|
*
|
|
|
|
|
* Is used in DI to configure the router.
|
|
|
|
|
*
|
2018-04-05 17:31:44 -04:00
|
|
|
|
*
|
2016-08-16 16:40:28 -04:00
|
|
|
|
*/
|
2017-01-03 19:54:46 -05:00
|
|
|
|
export const ROUTER_CONFIGURATION = new InjectionToken<ExtraOptions>('ROUTER_CONFIGURATION');
|
2016-08-16 16:40:28 -04:00
|
|
|
|
|
2016-09-10 19:53:27 -04:00
|
|
|
|
/**
|
|
|
|
|
* @docsNotRequired
|
|
|
|
|
*/
|
2017-01-03 19:54:46 -05:00
|
|
|
|
export const ROUTER_FORROOT_GUARD = new InjectionToken<void>('ROUTER_FORROOT_GUARD');
|
2016-08-23 14:57:58 -04:00
|
|
|
|
|
2016-08-24 16:39:44 -04:00
|
|
|
|
export const ROUTER_PROVIDERS: Provider[] = [
|
2016-11-09 17:58:40 -05:00
|
|
|
|
Location,
|
|
|
|
|
{provide: UrlSerializer, useClass: DefaultUrlSerializer},
|
|
|
|
|
{
|
2016-07-07 17:13:32 -04:00
|
|
|
|
provide: Router,
|
|
|
|
|
useFactory: setupRouter,
|
|
|
|
|
deps: [
|
2017-05-17 20:47:34 -04:00
|
|
|
|
ApplicationRef, UrlSerializer, ChildrenOutletContexts, Location, Injector,
|
|
|
|
|
NgModuleFactoryLoader, Compiler, ROUTES, ROUTER_CONFIGURATION,
|
|
|
|
|
[UrlHandlingStrategy, new Optional()], [RouteReuseStrategy, new Optional()]
|
2016-07-07 17:13:32 -04:00
|
|
|
|
]
|
|
|
|
|
},
|
2017-05-17 20:47:34 -04:00
|
|
|
|
ChildrenOutletContexts,
|
2016-11-09 17:58:40 -05:00
|
|
|
|
{provide: ActivatedRoute, useFactory: rootRoute, deps: [Router]},
|
|
|
|
|
{provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader},
|
|
|
|
|
RouterPreloader,
|
|
|
|
|
NoPreloading,
|
|
|
|
|
PreloadAllModules,
|
|
|
|
|
{provide: ROUTER_CONFIGURATION, useValue: {enableTracing: false}},
|
2016-07-07 17:13:32 -04:00
|
|
|
|
];
|
2016-07-06 18:36:50 -04:00
|
|
|
|
|
2016-11-09 17:58:40 -05:00
|
|
|
|
export function routerNgProbeToken() {
|
|
|
|
|
return new NgProbeToken('Router', Router);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-18 06:50:31 -04:00
|
|
|
|
/**
|
2018-04-05 06:38:57 -04:00
|
|
|
|
* @usageNotes
|
2016-07-27 12:54:19 -04:00
|
|
|
|
*
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* RouterModule can be imported multiple times: once per lazily-loaded bundle.
|
|
|
|
|
* Since the router deals with a global shared resource--location, we cannot have
|
|
|
|
|
* more than one router service active.
|
2016-07-18 06:50:31 -04:00
|
|
|
|
*
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* That is why there are two ways to create the module: `RouterModule.forRoot` and
|
|
|
|
|
* `RouterModule.forChild`.
|
|
|
|
|
*
|
|
|
|
|
* * `forRoot` creates a module that contains all the directives, the given routes, and the router
|
2016-11-09 17:58:40 -05:00
|
|
|
|
* service itself.
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* * `forChild` creates a module that contains all the directives and the given routes, but does not
|
2016-11-09 17:58:40 -05:00
|
|
|
|
* include the router service.
|
2016-09-10 19:53:27 -04:00
|
|
|
|
*
|
|
|
|
|
* When registered at the root, the module should be used as follows
|
2016-07-18 06:50:31 -04:00
|
|
|
|
*
|
|
|
|
|
* ```
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* @NgModule({
|
|
|
|
|
* imports: [RouterModule.forRoot(ROUTES)]
|
|
|
|
|
* })
|
|
|
|
|
* class MyNgModule {}
|
2016-07-18 06:50:31 -04:00
|
|
|
|
* ```
|
|
|
|
|
*
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* For submodules and lazy loaded submodules the module should be used as follows:
|
2016-07-06 18:36:50 -04:00
|
|
|
|
*
|
|
|
|
|
* ```
|
2016-07-27 12:54:19 -04:00
|
|
|
|
* @NgModule({
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* imports: [RouterModule.forChild(ROUTES)]
|
2016-07-27 12:54:19 -04:00
|
|
|
|
* })
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* class MyNgModule {}
|
2016-07-06 18:36:50 -04:00
|
|
|
|
* ```
|
|
|
|
|
*
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* @description
|
|
|
|
|
*
|
2018-04-05 06:51:21 -04:00
|
|
|
|
* Adds router directives and providers.
|
|
|
|
|
*
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* Managing state transitions is one of the hardest parts of building applications. This is
|
|
|
|
|
* especially true on the web, where you also need to ensure that the state is reflected in the URL.
|
|
|
|
|
* In addition, we often want to split applications into multiple bundles and load them on demand.
|
|
|
|
|
* Doing this transparently is not trivial.
|
|
|
|
|
*
|
2017-01-27 01:30:42 -05:00
|
|
|
|
* The Angular router solves these problems. Using the router, you can declaratively specify
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* application states, manage state transitions while taking care of the URL, and load bundles on
|
|
|
|
|
* demand.
|
|
|
|
|
*
|
|
|
|
|
* [Read this developer guide](https://angular.io/docs/ts/latest/guide/router.html) to get an
|
|
|
|
|
* overview of how the router should be used.
|
|
|
|
|
*
|
2018-04-05 17:31:44 -04:00
|
|
|
|
*
|
2016-07-06 18:36:50 -04:00
|
|
|
|
*/
|
2018-04-06 18:56:36 -04:00
|
|
|
|
@NgModule({
|
|
|
|
|
declarations: ROUTER_DIRECTIVES,
|
|
|
|
|
exports: ROUTER_DIRECTIVES,
|
|
|
|
|
entryComponents: [EmptyOutletComponent]
|
|
|
|
|
})
|
2016-07-07 14:59:08 -04:00
|
|
|
|
export class RouterModule {
|
2017-03-14 17:32:26 -04:00
|
|
|
|
// Note: We are injecting the Router so it gets created eagerly...
|
|
|
|
|
constructor(@Optional() @Inject(ROUTER_FORROOT_GUARD) guard: any, @Optional() router: Router) {}
|
2016-08-23 14:57:58 -04:00
|
|
|
|
|
2016-09-10 19:53:27 -04:00
|
|
|
|
/**
|
|
|
|
|
* Creates a module with all the router providers and directives. It also optionally sets up an
|
|
|
|
|
* application listener to perform an initial navigation.
|
|
|
|
|
*
|
2018-04-05 06:53:57 -04:00
|
|
|
|
* Options (see `ExtraOptions`):
|
2016-09-10 19:53:27 -04:00
|
|
|
|
* * `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.
|
2018-04-05 06:53:57 -04:00
|
|
|
|
* * `preloadingStrategy` configures a preloading strategy (see `PreloadAllModules`).
|
2017-10-19 18:32:50 -04:00
|
|
|
|
* * `onSameUrlNavigation` configures how the router handles navigation to the current URL. See
|
2018-04-05 06:53:57 -04:00
|
|
|
|
* `ExtraOptions` for more details.
|
2018-03-05 08:34:55 -05:00
|
|
|
|
* * `paramsInheritanceStrategy` defines how the router merges params, data and resolved data
|
|
|
|
|
* from parent to child routes.
|
2016-09-10 19:53:27 -04:00
|
|
|
|
*/
|
2016-07-27 12:54:19 -04:00
|
|
|
|
static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders {
|
|
|
|
|
return {
|
|
|
|
|
ngModule: RouterModule,
|
|
|
|
|
providers: [
|
2016-11-09 17:58:40 -05:00
|
|
|
|
ROUTER_PROVIDERS,
|
|
|
|
|
provideRoutes(routes),
|
|
|
|
|
{
|
2016-08-23 14:57:58 -04:00
|
|
|
|
provide: ROUTER_FORROOT_GUARD,
|
|
|
|
|
useFactory: provideForRootGuard,
|
|
|
|
|
deps: [[Router, new Optional(), new SkipSelf()]]
|
|
|
|
|
},
|
2016-11-09 17:58:40 -05:00
|
|
|
|
{provide: ROUTER_CONFIGURATION, useValue: config ? config : {}},
|
|
|
|
|
{
|
2016-07-28 15:10:36 -04:00
|
|
|
|
provide: LocationStrategy,
|
|
|
|
|
useFactory: provideLocationStrategy,
|
2016-07-28 17:36:05 -04:00
|
|
|
|
deps: [
|
|
|
|
|
PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], ROUTER_CONFIGURATION
|
|
|
|
|
]
|
2016-08-02 08:22:44 -04:00
|
|
|
|
},
|
2018-05-17 07:33:50 -04:00
|
|
|
|
{
|
|
|
|
|
provide: RouterScroller,
|
|
|
|
|
useFactory: createRouterScroller,
|
|
|
|
|
deps: [Router, ViewportScroller, ROUTER_CONFIGURATION]
|
|
|
|
|
},
|
2016-09-16 18:08:15 -04:00
|
|
|
|
{
|
|
|
|
|
provide: PreloadingStrategy,
|
|
|
|
|
useExisting: config && config.preloadingStrategy ? config.preloadingStrategy :
|
|
|
|
|
NoPreloading
|
|
|
|
|
},
|
2016-11-09 17:58:40 -05:00
|
|
|
|
{provide: NgProbeToken, multi: true, useFactory: routerNgProbeToken},
|
|
|
|
|
provideRouterInitializer(),
|
|
|
|
|
],
|
2016-07-27 12:54:19 -04:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-10 19:53:27 -04:00
|
|
|
|
/**
|
|
|
|
|
* Creates a module with all the router directives and a provider registering routes.
|
|
|
|
|
*/
|
2016-07-27 12:54:19 -04:00
|
|
|
|
static forChild(routes: Routes): ModuleWithProviders {
|
|
|
|
|
return {ngModule: RouterModule, providers: [provideRoutes(routes)]};
|
|
|
|
|
}
|
2016-07-18 06:50:31 -04:00
|
|
|
|
}
|
2016-07-28 15:10:36 -04:00
|
|
|
|
|
2018-05-17 07:33:50 -04:00
|
|
|
|
export function createRouterScroller(
|
|
|
|
|
router: Router, viewportScroller: ViewportScroller, config: ExtraOptions): RouterScroller {
|
|
|
|
|
if (config.scrollOffset) {
|
|
|
|
|
viewportScroller.setOffset(config.scrollOffset);
|
|
|
|
|
}
|
|
|
|
|
return new RouterScroller(router, viewportScroller, config);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-01 18:51:22 -04:00
|
|
|
|
export function provideLocationStrategy(
|
2016-07-28 15:10:36 -04:00
|
|
|
|
platformLocationStrategy: PlatformLocation, baseHref: string, options: ExtraOptions = {}) {
|
|
|
|
|
return options.useHash ? new HashLocationStrategy(platformLocationStrategy, baseHref) :
|
|
|
|
|
new PathLocationStrategy(platformLocationStrategy, baseHref);
|
2016-08-16 16:40:28 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 14:57:58 -04:00
|
|
|
|
export function provideForRootGuard(router: Router): any {
|
|
|
|
|
if (router) {
|
2016-08-25 03:50:16 -04:00
|
|
|
|
throw new Error(
|
2016-08-23 14:57:58 -04:00
|
|
|
|
`RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`);
|
|
|
|
|
}
|
|
|
|
|
return 'guarded';
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-16 16:40:28 -04:00
|
|
|
|
/**
|
2018-04-05 06:38:57 -04:00
|
|
|
|
* @description
|
|
|
|
|
*
|
2018-04-05 06:51:21 -04:00
|
|
|
|
* Registers routes.
|
|
|
|
|
*
|
2018-04-05 06:38:57 -04:00
|
|
|
|
* ### Example
|
2016-09-10 19:53:27 -04:00
|
|
|
|
*
|
|
|
|
|
* ```
|
|
|
|
|
* @NgModule({
|
|
|
|
|
* imports: [RouterModule.forChild(ROUTES)],
|
|
|
|
|
* providers: [provideRoutes(EXTRA_ROUTES)]
|
|
|
|
|
* })
|
|
|
|
|
* class MyNgModule {}
|
|
|
|
|
* ```
|
|
|
|
|
*
|
2018-04-05 17:31:44 -04:00
|
|
|
|
*
|
2016-08-16 16:40:28 -04:00
|
|
|
|
*/
|
|
|
|
|
export function provideRoutes(routes: Routes): any {
|
|
|
|
|
return [
|
|
|
|
|
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes},
|
2016-11-09 17:58:40 -05:00
|
|
|
|
{provide: ROUTES, multi: true, useValue: routes},
|
2016-08-16 16:40:28 -04:00
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-07 17:27:20 -05:00
|
|
|
|
/**
|
|
|
|
|
* @description
|
2018-04-05 06:51:21 -04:00
|
|
|
|
*
|
|
|
|
|
* Represents an option to configure when the initial navigation is performed.
|
|
|
|
|
*
|
2017-03-07 17:27:20 -05:00
|
|
|
|
* * 'enabled' - the initial navigation starts before the root component is created.
|
|
|
|
|
* The bootstrap is blocked until the initial navigation is complete.
|
|
|
|
|
* * 'disabled' - the initial navigation is not performed. The location listener is set up before
|
|
|
|
|
* the root component gets created.
|
2017-08-22 19:39:06 -04:00
|
|
|
|
* * 'legacy_enabled'- the initial navigation starts after the root component has been created.
|
|
|
|
|
* The bootstrap is not blocked until the initial navigation is complete. @deprecated
|
|
|
|
|
* * 'legacy_disabled'- the initial navigation is not performed. The location listener is set up
|
|
|
|
|
* after @deprecated
|
|
|
|
|
* the root component gets created.
|
|
|
|
|
* * `true` - same as 'legacy_enabled'. @deprecated since v4
|
|
|
|
|
* * `false` - same as 'legacy_disabled'. @deprecated since v4
|
2017-03-07 17:27:20 -05:00
|
|
|
|
*
|
|
|
|
|
* The 'enabled' option should be used for applications unless there is a reason to have
|
|
|
|
|
* more control over when the router starts its initial navigation due to some complex
|
|
|
|
|
* initialization logic. In this case, 'disabled' should be used.
|
|
|
|
|
*
|
2017-08-22 19:39:06 -04:00
|
|
|
|
* The 'legacy_enabled' and 'legacy_disabled' should not be used for new applications.
|
|
|
|
|
*
|
2017-03-07 17:27:20 -05:00
|
|
|
|
* @experimental
|
|
|
|
|
*/
|
2017-08-22 19:39:06 -04:00
|
|
|
|
export type InitialNavigation =
|
|
|
|
|
true | false | 'enabled' | 'disabled' | 'legacy_enabled' | 'legacy_disabled';
|
2016-08-16 16:40:28 -04:00
|
|
|
|
|
|
|
|
|
/**
|
2018-04-05 06:51:21 -04:00
|
|
|
|
* @description
|
|
|
|
|
*
|
|
|
|
|
* Represents options to configure the router.
|
2016-08-25 10:56:30 -04:00
|
|
|
|
*
|
2018-04-05 17:31:44 -04:00
|
|
|
|
*
|
2016-08-16 16:40:28 -04:00
|
|
|
|
*/
|
|
|
|
|
export interface ExtraOptions {
|
2016-09-10 19:53:27 -04:00
|
|
|
|
/**
|
|
|
|
|
* Makes the router log all its internal events to the console.
|
|
|
|
|
*/
|
2016-08-16 16:40:28 -04:00
|
|
|
|
enableTracing?: boolean;
|
2016-09-10 19:53:27 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enables the location strategy that uses the URL fragment instead of the history API.
|
|
|
|
|
*/
|
2016-08-16 16:40:28 -04:00
|
|
|
|
useHash?: boolean;
|
2016-09-10 19:53:27 -04:00
|
|
|
|
|
|
|
|
|
/**
|
2017-08-22 19:39:06 -04:00
|
|
|
|
* Disables the initial navigation.
|
2016-09-10 19:53:27 -04:00
|
|
|
|
*/
|
2017-03-07 17:27:20 -05:00
|
|
|
|
initialNavigation?: InitialNavigation;
|
2016-09-10 19:53:27 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A custom error handler.
|
|
|
|
|
*/
|
2016-08-25 10:56:30 -04:00
|
|
|
|
errorHandler?: ErrorHandler;
|
2016-09-16 18:08:15 -04:00
|
|
|
|
|
|
|
|
|
/**
|
2018-04-05 06:53:57 -04:00
|
|
|
|
* Configures a preloading strategy. See `PreloadAllModules`.
|
2016-09-16 18:08:15 -04:00
|
|
|
|
*/
|
|
|
|
|
preloadingStrategy?: any;
|
2017-10-19 18:32:50 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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';
|
2017-11-28 19:57:10 -05:00
|
|
|
|
|
2018-05-17 07:33:50 -04:00
|
|
|
|
/**
|
|
|
|
|
* Configures if the scroll position needs to be restored when navigating back.
|
|
|
|
|
*
|
|
|
|
|
* * 'disabled'--does nothing (default).
|
|
|
|
|
* * 'top'--set the scroll position to 0,0..
|
|
|
|
|
* * 'enabled'--set the scroll position to the stored position. This option will be the default in
|
|
|
|
|
* the future.
|
|
|
|
|
*
|
|
|
|
|
* When enabled, the router store store scroll positions when navigating forward, and will
|
|
|
|
|
* restore the stored positions whe navigating back (popstate). When navigating forward,
|
|
|
|
|
* the scroll position will be set to [0, 0], or to the anchor if one is provided.
|
|
|
|
|
*
|
|
|
|
|
* You can implement custom scroll restoration behavior as follows.
|
|
|
|
|
* ```typescript
|
|
|
|
|
* class AppModule {
|
|
|
|
|
* constructor(router: Router, viewportScroller: ViewportScroller, store: Store<AppState>) {
|
|
|
|
|
* router.events.pipe(filter(e => e instanceof Scroll), switchMap(e => {
|
|
|
|
|
* return store.pipe(first(), timeout(200), map(() => e));
|
|
|
|
|
* }).subscribe(e => {
|
|
|
|
|
* if (e.position) {
|
|
|
|
|
* viewportScroller.scrollToPosition(e.position);
|
|
|
|
|
* } else if (e.anchor) {
|
|
|
|
|
* viewportScroller.scrollToAnchor(e.anchor);
|
|
|
|
|
* } else {
|
|
|
|
|
* viewportScroller.scrollToPosition([0, 0]);
|
|
|
|
|
* }
|
|
|
|
|
* });
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* You can also implement component-specific scrolling like this:
|
|
|
|
|
*
|
|
|
|
|
* ```typescript
|
|
|
|
|
* class ListComponent {
|
|
|
|
|
* list: any[];
|
|
|
|
|
* constructor(router: Router, viewportScroller: ViewportScroller, fetcher: ListFetcher) {
|
|
|
|
|
* const scrollEvents = router.events.filter(e => e instanceof Scroll);
|
|
|
|
|
* listFetcher.fetch().pipe(withLatestFrom(scrollEvents)).subscribe(([list, e]) => {
|
|
|
|
|
* this.list = list;
|
|
|
|
|
* if (e.position) {
|
|
|
|
|
* viewportScroller.scrollToPosition(e.position);
|
|
|
|
|
* } else {
|
|
|
|
|
* viewportScroller.scrollToPosition([0, 0]);
|
|
|
|
|
* }
|
|
|
|
|
* });
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
scrollPositionRestoration?: 'disabled'|'enabled'|'top';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configures if the router should scroll to the element when the url has a fragment.
|
|
|
|
|
*
|
|
|
|
|
* * 'disabled'--does nothing (default).
|
|
|
|
|
* * 'enabled'--scrolls to the element. This option will be the default in the future.
|
|
|
|
|
*
|
|
|
|
|
* Anchor scrolling does not happen on 'popstate'. Instead, we restore the position
|
|
|
|
|
* that we stored or scroll to the top.
|
|
|
|
|
*/
|
|
|
|
|
anchorScrolling?: 'disabled'|'enabled';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Configures the scroll offset the router will use when scrolling to an element.
|
|
|
|
|
*
|
|
|
|
|
* When given a tuple with two numbers, the router will always use the numbers.
|
|
|
|
|
* When given a function, the router will invoke the function every time it restores scroll
|
|
|
|
|
* position.
|
|
|
|
|
*/
|
|
|
|
|
scrollOffset?: [number, number]|(() => [number, number]);
|
|
|
|
|
|
2017-11-28 19:57:10 -05:00
|
|
|
|
/**
|
|
|
|
|
* Defines how the router merges params, data and resolved data from parent to child
|
|
|
|
|
* routes. Available options are:
|
|
|
|
|
*
|
|
|
|
|
* - `'emptyOnly'`, the default, only inherits parent params for path-less or component-less
|
|
|
|
|
* routes.
|
|
|
|
|
* - `'always'`, enables unconditional inheritance of parent params.
|
|
|
|
|
*/
|
|
|
|
|
paramsInheritanceStrategy?: 'emptyOnly'|'always';
|
2018-04-10 06:01:07 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A custom malformed uri error handler function. This handler is invoked when encodedURI contains
|
|
|
|
|
* invalid character sequences. The default implementation is to redirect to the root url dropping
|
|
|
|
|
* any path or param info. This function passes three parameters:
|
|
|
|
|
*
|
|
|
|
|
* - `'URIError'` - Error thrown when parsing a bad URL
|
|
|
|
|
* - `'UrlSerializer'` - UrlSerializer that’s configured with the router.
|
|
|
|
|
* - `'url'` - The malformed URL that caused the URIError
|
|
|
|
|
* */
|
|
|
|
|
malformedUriErrorHandler?:
|
|
|
|
|
(error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree;
|
2018-07-10 12:44:15 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Defines when the router updates the browser URL. The default behavior is to update after
|
|
|
|
|
* successful navigation. However, some applications may prefer a mode where the URL gets
|
|
|
|
|
* updated at the beginning of navigation. The most common use case would be updating the
|
|
|
|
|
* URL early so if navigation fails, you can show an error message with the URL that failed.
|
|
|
|
|
* Available options are:
|
|
|
|
|
*
|
|
|
|
|
* - `'deferred'`, the default, updates the browser URL after navigation has finished.
|
|
|
|
|
* - `'eager'`, updates browser URL at the beginning of navigation.
|
|
|
|
|
*/
|
|
|
|
|
urlUpdateStrategy?: 'deferred'|'eager';
|
2016-08-16 16:40:28 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setupRouter(
|
2017-05-17 20:47:34 -04:00
|
|
|
|
ref: ApplicationRef, urlSerializer: UrlSerializer, contexts: ChildrenOutletContexts,
|
2016-08-16 16:40:28 -04:00
|
|
|
|
location: Location, injector: Injector, loader: NgModuleFactoryLoader, compiler: Compiler,
|
2016-11-30 02:21:41 -05:00
|
|
|
|
config: Route[][], opts: ExtraOptions = {}, urlHandlingStrategy?: UrlHandlingStrategy,
|
|
|
|
|
routeReuseStrategy?: RouteReuseStrategy) {
|
2016-10-20 13:44:44 -04:00
|
|
|
|
const router = new Router(
|
2017-05-17 20:47:34 -04:00
|
|
|
|
null, urlSerializer, contexts, location, injector, loader, compiler, flatten(config));
|
2016-08-16 16:40:28 -04:00
|
|
|
|
|
2016-10-20 13:44:44 -04:00
|
|
|
|
if (urlHandlingStrategy) {
|
|
|
|
|
router.urlHandlingStrategy = urlHandlingStrategy;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-30 02:21:41 -05:00
|
|
|
|
if (routeReuseStrategy) {
|
|
|
|
|
router.routeReuseStrategy = routeReuseStrategy;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-25 10:56:30 -04:00
|
|
|
|
if (opts.errorHandler) {
|
2016-10-20 13:44:44 -04:00
|
|
|
|
router.errorHandler = opts.errorHandler;
|
2016-08-25 10:56:30 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 06:01:07 -04:00
|
|
|
|
if (opts.malformedUriErrorHandler) {
|
|
|
|
|
router.malformedUriErrorHandler = opts.malformedUriErrorHandler;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-16 16:40:28 -04:00
|
|
|
|
if (opts.enableTracing) {
|
2016-11-11 13:46:53 -05:00
|
|
|
|
const dom = getDOM();
|
2018-02-27 17:06:06 -05:00
|
|
|
|
router.events.subscribe((e: RouterEvent) => {
|
2016-11-11 13:46:53 -05:00
|
|
|
|
dom.logGroup(`Router Event: ${(<any>e.constructor).name}`);
|
|
|
|
|
dom.log(e.toString());
|
|
|
|
|
dom.log(e);
|
|
|
|
|
dom.logGroupEnd();
|
2016-08-16 16:40:28 -04:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-19 18:32:50 -04:00
|
|
|
|
if (opts.onSameUrlNavigation) {
|
|
|
|
|
router.onSameUrlNavigation = opts.onSameUrlNavigation;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-28 19:57:10 -05:00
|
|
|
|
if (opts.paramsInheritanceStrategy) {
|
|
|
|
|
router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-10 12:44:15 -04:00
|
|
|
|
if (opts.urlUpdateStrategy) {
|
|
|
|
|
router.urlUpdateStrategy = opts.urlUpdateStrategy;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-20 13:44:44 -04:00
|
|
|
|
return router;
|
2016-08-16 16:40:28 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function rootRoute(router: Router): ActivatedRoute {
|
|
|
|
|
return router.routerState.root;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-07 17:27:20 -05:00
|
|
|
|
/**
|
|
|
|
|
* To initialize the router properly we need to do in two steps:
|
|
|
|
|
*
|
|
|
|
|
* We need to start the navigation in a APP_INITIALIZER to block the bootstrap if
|
|
|
|
|
* a resolver or a guards executes asynchronously. Second, we need to actually run
|
|
|
|
|
* activation in a BOOTSTRAP_LISTENER. We utilize the afterPreactivation
|
|
|
|
|
* hook provided by the router to do that.
|
|
|
|
|
*
|
|
|
|
|
* The router navigation starts, reaches the point when preactivation is done, and then
|
|
|
|
|
* pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener.
|
|
|
|
|
*/
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class RouterInitializer {
|
|
|
|
|
private initNavigation: boolean = false;
|
|
|
|
|
private resultOfPreactivationDone = new Subject<void>();
|
|
|
|
|
|
|
|
|
|
constructor(private injector: Injector) {}
|
|
|
|
|
|
|
|
|
|
appInitializer(): Promise<any> {
|
|
|
|
|
const p: Promise<any> = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
|
|
|
|
|
return p.then(() => {
|
2017-04-17 14:13:13 -04:00
|
|
|
|
let resolve: Function = null !;
|
2017-03-07 17:27:20 -05:00
|
|
|
|
const res = new Promise(r => resolve = r);
|
|
|
|
|
const router = this.injector.get(Router);
|
|
|
|
|
const opts = this.injector.get(ROUTER_CONFIGURATION);
|
|
|
|
|
|
2017-08-22 19:39:06 -04:00
|
|
|
|
if (this.isLegacyDisabled(opts) || this.isLegacyEnabled(opts)) {
|
|
|
|
|
resolve(true);
|
|
|
|
|
|
|
|
|
|
} else if (opts.initialNavigation === 'disabled') {
|
2017-03-07 17:27:20 -05:00
|
|
|
|
router.setUpLocationChangeListener();
|
|
|
|
|
resolve(true);
|
|
|
|
|
|
2017-08-22 19:39:06 -04:00
|
|
|
|
} else if (opts.initialNavigation === 'enabled') {
|
2017-03-07 17:27:20 -05:00
|
|
|
|
router.hooks.afterPreactivation = () => {
|
|
|
|
|
// only the initial navigation should be delayed
|
|
|
|
|
if (!this.initNavigation) {
|
|
|
|
|
this.initNavigation = true;
|
|
|
|
|
resolve(true);
|
|
|
|
|
return this.resultOfPreactivationDone;
|
|
|
|
|
|
|
|
|
|
// subsequent navigations should not be delayed
|
|
|
|
|
} else {
|
2017-04-17 14:13:13 -04:00
|
|
|
|
return of (null) as any;
|
2017-03-07 17:27:20 -05:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
router.initialNavigation();
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`Invalid initialNavigation options: '${opts.initialNavigation}'`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bootstrapListener(bootstrappedComponentRef: ComponentRef<any>): void {
|
|
|
|
|
const opts = this.injector.get(ROUTER_CONFIGURATION);
|
|
|
|
|
const preloader = this.injector.get(RouterPreloader);
|
2018-05-17 07:33:50 -04:00
|
|
|
|
const routerScroller = this.injector.get(RouterScroller);
|
2017-03-07 17:27:20 -05:00
|
|
|
|
const router = this.injector.get(Router);
|
2017-11-15 11:43:35 -05:00
|
|
|
|
const ref = this.injector.get<ApplicationRef>(ApplicationRef);
|
2016-10-16 14:33:50 -04:00
|
|
|
|
|
|
|
|
|
if (bootstrappedComponentRef !== ref.components[0]) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-22 19:39:06 -04:00
|
|
|
|
if (this.isLegacyEnabled(opts)) {
|
|
|
|
|
router.initialNavigation();
|
|
|
|
|
} else if (this.isLegacyDisabled(opts)) {
|
|
|
|
|
router.setUpLocationChangeListener();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-07 17:27:20 -05:00
|
|
|
|
preloader.setUpPreloading();
|
2018-05-17 07:33:50 -04:00
|
|
|
|
routerScroller.init();
|
2017-03-07 17:27:20 -05:00
|
|
|
|
router.resetRootComponentType(ref.componentTypes[0]);
|
2017-04-17 14:13:13 -04:00
|
|
|
|
this.resultOfPreactivationDone.next(null !);
|
2017-03-07 17:27:20 -05:00
|
|
|
|
this.resultOfPreactivationDone.complete();
|
|
|
|
|
}
|
2017-08-22 19:39:06 -04:00
|
|
|
|
|
|
|
|
|
private isLegacyEnabled(opts: ExtraOptions): boolean {
|
|
|
|
|
return opts.initialNavigation === 'legacy_enabled' || opts.initialNavigation === true ||
|
|
|
|
|
opts.initialNavigation === undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private isLegacyDisabled(opts: ExtraOptions): boolean {
|
|
|
|
|
return opts.initialNavigation === 'legacy_disabled' || opts.initialNavigation === false;
|
|
|
|
|
}
|
2017-03-07 17:27:20 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getAppInitializer(r: RouterInitializer) {
|
|
|
|
|
return r.appInitializer.bind(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getBootstrapListener(r: RouterInitializer) {
|
|
|
|
|
return r.bootstrapListener.bind(r);
|
2016-08-16 16:40:28 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-09 16:33:33 -05:00
|
|
|
|
/**
|
|
|
|
|
* A token for the router initializer that will be called after the app is bootstrapped.
|
|
|
|
|
*
|
|
|
|
|
* @experimental
|
|
|
|
|
*/
|
2017-01-03 19:54:46 -05:00
|
|
|
|
export const ROUTER_INITIALIZER =
|
|
|
|
|
new InjectionToken<(compRef: ComponentRef<any>) => void>('Router Initializer');
|
2016-11-09 16:33:33 -05:00
|
|
|
|
|
2016-08-16 16:40:28 -04:00
|
|
|
|
export function provideRouterInitializer() {
|
2016-11-09 16:33:33 -05:00
|
|
|
|
return [
|
2017-03-07 17:27:20 -05:00
|
|
|
|
RouterInitializer,
|
2016-11-09 16:33:33 -05:00
|
|
|
|
{
|
2017-03-07 17:27:20 -05:00
|
|
|
|
provide: APP_INITIALIZER,
|
|
|
|
|
multi: true,
|
|
|
|
|
useFactory: getAppInitializer,
|
|
|
|
|
deps: [RouterInitializer]
|
2016-11-09 16:33:33 -05:00
|
|
|
|
},
|
2017-03-07 17:27:20 -05:00
|
|
|
|
{provide: ROUTER_INITIALIZER, useFactory: getBootstrapListener, deps: [RouterInitializer]},
|
2016-11-09 17:58:40 -05:00
|
|
|
|
{provide: APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER},
|
2016-11-09 16:33:33 -05:00
|
|
|
|
];
|
2016-08-19 18:48:09 -04:00
|
|
|
|
}
|