diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts index a3d6d9b926..cb5d05a5a7 100644 --- a/public/docs/_examples/router/e2e-spec.ts +++ b/public/docs/_examples/router/e2e-spec.ts @@ -1,4 +1,4 @@ -'use strict'; // necessary for es6 output in node +'use strict'; // necessary for es6 output in node import { browser, element, by, ElementFinder } from 'protractor'; @@ -27,7 +27,10 @@ describe('Router', function () { heroDetailTitle: element(by.css('my-app > ng-component > div > h3')), adminHref: hrefEles.get(2), - loginHref: hrefEles.get(3) + adminPreloadList: element.all(by.css('my-app > ng-component > ng-component > ul > li')), + loginHref: hrefEles.get(3), + loginButton: element.all(by.css('my-app > ng-component > p > button')), + }; } @@ -105,6 +108,16 @@ describe('Router', function () { }); }); + it('should be able to see the preloaded modules', function () { + let page = getPageStruct(); + page.loginHref.click().then(function() { + return page.loginButton.click(); + }).then(function() { + expect(page.adminPreloadList.count()).toBe(1, 'should be 1 preloaded module'); + expect(page.adminPreloadList.first().getText()).toBe('crisis-center', 'first preload should be crisis center'); + }); + }); + function crisisCenterEdit(index: number, shouldSave: boolean) { let page = getPageStruct(); let crisisEle: ElementFinder; diff --git a/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.2.ts b/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.2.ts new file mode 100644 index 0000000000..8c8e481643 --- /dev/null +++ b/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.2.ts @@ -0,0 +1,33 @@ +// #docregion +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/map'; + +@Component({ + template: ` +

Dashboard

+ +

Session ID: {{ sessionId | async }}

+ +

Token: {{ token | async }}

+ ` +}) +export class AdminDashboardComponent implements OnInit { + sessionId: Observable; + token: Observable; + + constructor(private route: ActivatedRoute) {} + + ngOnInit() { + // Capture the session ID if available + this.sessionId = this.route + .queryParams + .map(params => params['session_id'] || 'None'); + + // Capture the fragment if available + this.token = this.route + .fragment + .map(fragment => fragment || 'None'); + } +} diff --git a/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.ts b/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.ts index 8c8e481643..02574829c0 100644 --- a/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.ts +++ b/public/docs/_examples/router/ts/app/admin/admin-dashboard.component.ts @@ -1,7 +1,9 @@ // #docregion -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { PreloadSelectedModules } from '../selective-preload-strategy'; + import 'rxjs/add/operator/map'; @Component({ @@ -11,13 +13,24 @@ import 'rxjs/add/operator/map';

Session ID: {{ sessionId | async }}

Token: {{ token | async }}

+ + Preloaded Modules + ` }) export class AdminDashboardComponent implements OnInit { sessionId: Observable; token: Observable; + modules: string[]; - constructor(private route: ActivatedRoute) {} + constructor( + private route: ActivatedRoute, + private preloadStrategy: PreloadSelectedModules + ) { + this.modules = preloadStrategy.preloadedModules; + } ngOnInit() { // Capture the session ID if available diff --git a/public/docs/_examples/router/ts/app/app-routing.module.5.ts b/public/docs/_examples/router/ts/app/app-routing.module.5.ts new file mode 100644 index 0000000000..6e35f64f73 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app-routing.module.5.ts @@ -0,0 +1,33 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +// #docregion import-router +import { RouterModule } from '@angular/router'; +// #enddocregion import-router + +import { CanDeactivateGuard } from './can-deactivate-guard.service'; +// #docregion can-load-guard +import { AuthGuard } from './auth-guard.service'; +// #enddocregion can-load-guard + +// #docregion lazy-load-admin, can-load-guard +@NgModule({ + imports: [ + RouterModule.forRoot([ + { + path: 'admin', + loadChildren: 'app/admin/admin.module#AdminModule', + // #enddocregion lazy-load-admin + canLoad: [AuthGuard] + // #docregion lazy-load-admin + } + ]) + ], + exports: [ + RouterModule + ], + providers: [ + CanDeactivateGuard + ] +}) +export class AppRoutingModule {} diff --git a/public/docs/_examples/router/ts/app/app-routing.module.6.ts b/public/docs/_examples/router/ts/app/app-routing.module.6.ts new file mode 100644 index 0000000000..68bcc30413 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app-routing.module.6.ts @@ -0,0 +1,44 @@ +// #docplaster +// #docregion, preload-v1 +import { NgModule } from '@angular/core'; +import { + RouterModule, +// #enddocregion preload-v1 + PreloadAllModules +// #docregion preload-v1 +} from '@angular/router'; + +import { CanDeactivateGuard } from './can-deactivate-guard.service'; +import { AuthGuard } from './auth-guard.service'; + +@NgModule({ + imports: [ + RouterModule.forRoot([ + { + path: 'admin', + loadChildren: 'app/admin/admin.module#AdminModule', + canLoad: [AuthGuard] + }, + { + path: '', + redirectTo: '/heroes', + pathMatch: 'full' + }, + { + path: 'crisis-center', + loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule' + }, + ], + // #enddocregion preload-v1 + { preloadingStrategy: PreloadAllModules } + // #docregion preload-v1 + ) + ], + exports: [ + RouterModule + ], + providers: [ + CanDeactivateGuard + ] +}) +export class AppRoutingModule {} diff --git a/public/docs/_examples/router/ts/app/app-routing.module.ts b/public/docs/_examples/router/ts/app/app-routing.module.ts index 6e35f64f73..8fa63edb1c 100644 --- a/public/docs/_examples/router/ts/app/app-routing.module.ts +++ b/public/docs/_examples/router/ts/app/app-routing.module.ts @@ -1,33 +1,43 @@ // #docplaster -// #docregion +// #docregion, preload-v1 import { NgModule } from '@angular/core'; -// #docregion import-router import { RouterModule } from '@angular/router'; -// #enddocregion import-router import { CanDeactivateGuard } from './can-deactivate-guard.service'; -// #docregion can-load-guard import { AuthGuard } from './auth-guard.service'; -// #enddocregion can-load-guard +import { PreloadSelectedModules } from './selective-preload-strategy'; -// #docregion lazy-load-admin, can-load-guard @NgModule({ imports: [ RouterModule.forRoot([ { path: 'admin', loadChildren: 'app/admin/admin.module#AdminModule', - // #enddocregion lazy-load-admin canLoad: [AuthGuard] - // #docregion lazy-load-admin + }, + { + path: '', + redirectTo: '/heroes', + pathMatch: 'full' + }, + // #docregion preload-v2 + { + path: 'crisis-center', + loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule', + data: { + preload: true + } } - ]) + // #enddocregion preload-v2 + ], + { preloadingStrategy: PreloadSelectedModules }) ], exports: [ RouterModule ], providers: [ - CanDeactivateGuard + CanDeactivateGuard, + PreloadSelectedModules ] }) export class AppRoutingModule {} diff --git a/public/docs/_examples/router/ts/app/app.module.7.ts b/public/docs/_examples/router/ts/app/app.module.7.ts index 03f13cc432..8313e3aacf 100644 --- a/public/docs/_examples/router/ts/app/app.module.7.ts +++ b/public/docs/_examples/router/ts/app/app.module.7.ts @@ -9,6 +9,8 @@ import { AppRoutingModule } from './app-routing.module'; import { HeroesModule } from './heroes/heroes.module'; import { CrisisCenterModule } from './crisis-center/crisis-center.module'; import { LoginRoutingModule } from './login-routing.module'; +import { LoginComponent } from './login.component'; + import { DialogService } from './dialog.service'; @NgModule({ @@ -21,7 +23,8 @@ import { DialogService } from './dialog.service'; AppRoutingModule ], declarations: [ - AppComponent + AppComponent, + LoginComponent ], providers: [ DialogService diff --git a/public/docs/_examples/router/ts/app/app.module.ts b/public/docs/_examples/router/ts/app/app.module.ts index e9b4726c74..e557abfb30 100644 --- a/public/docs/_examples/router/ts/app/app.module.ts +++ b/public/docs/_examples/router/ts/app/app.module.ts @@ -5,11 +5,9 @@ import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { LoginRoutingModule } from './login-routing.module'; import { HeroesModule } from './heroes/heroes.module'; -import { CrisisCenterModule } from './crisis-center/crisis-center.module'; - +import { LoginRoutingModule } from './login-routing.module'; import { LoginComponent } from './login.component'; import { DialogService } from './dialog.service'; @@ -19,7 +17,6 @@ import { DialogService } from './dialog.service'; BrowserModule, FormsModule, HeroesModule, - CrisisCenterModule, LoginRoutingModule, AppRoutingModule ], diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.4.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.4.ts new file mode 100644 index 0000000000..17843ace3e --- /dev/null +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.4.ts @@ -0,0 +1,60 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { CrisisCenterHomeComponent } from './crisis-center-home.component'; +import { CrisisListComponent } from './crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail.component'; + +import { CanDeactivateGuard } from '../can-deactivate-guard.service'; + +// #docregion crisis-detail-resolve +import { CrisisDetailResolve } from './crisis-detail-resolve.service'; + +@NgModule({ + imports: [ + RouterModule.forChild([ + // #docregion redirect + { + path: '', + redirectTo: '/crisis-center', + pathMatch: 'full' + }, + // #enddocregion redirect + { + path: 'crisis-center', + component: CrisisCenterComponent, + children: [ + { + path: '', + component: CrisisListComponent, + children: [ + { + path: ':id', + component: CrisisDetailComponent, + canDeactivate: [CanDeactivateGuard], + resolve: { + crisis: CrisisDetailResolve + } + }, + { + path: '', + component: CrisisCenterHomeComponent + } + ] + } + ] + } + ]) + ], + exports: [ + RouterModule + ], + providers: [ + CrisisDetailResolve + ] +}) +export class CrisisCenterRoutingModule { } +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.ts index 17843ace3e..e1d2ae1207 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center-routing.module.ts @@ -16,15 +16,8 @@ import { CrisisDetailResolve } from './crisis-detail-resolve.service'; @NgModule({ imports: [ RouterModule.forChild([ - // #docregion redirect { path: '', - redirectTo: '/crisis-center', - pathMatch: 'full' - }, - // #enddocregion redirect - { - path: 'crisis-center', component: CrisisCenterComponent, children: [ { diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts index c23c45ab58..2a52513b6f 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts @@ -35,14 +35,14 @@ import { DialogService } from '../dialog.service'; transform: 'translateX(0)' }) ), - transition('void => *', [ + transition(':enter', [ style({ opacity: 0, transform: 'translateX(-100%)' }), animate('0.2s ease-in') ]), - transition('* => void', [ + transition(':leave', [ animate('0.5s ease-out', style({ opacity: 0, transform: 'translateY(100%)' diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts index 17ae9ff195..cda3b7dc6a 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts @@ -34,14 +34,14 @@ import { DialogService } from '../dialog.service'; transform: 'translateX(0)' }) ), - transition('void => *', [ + transition(':enter', [ style({ opacity: 0, transform: 'translateX(-100%)' }), animate('0.2s ease-in') ]), - transition('* => void', [ + transition(':leave', [ animate('0.5s ease-out', style({ opacity: 0, transform: 'translateY(100%)' diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts index a263fe65f0..42adbe2d98 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts @@ -34,14 +34,14 @@ import { Hero, HeroService } from './hero.service'; transform: 'translateX(0)' }) ), - transition('void => *', [ + transition(':enter', [ style({ opacity: 0, transform: 'translateX(-100%)' }), animate('0.2s ease-in') ]), - transition('* => void', [ + transition(':leave', [ animate('0.5s ease-out', style({ opacity: 0, transform: 'translateY(100%)' diff --git a/public/docs/_examples/router/ts/app/selective-preload-strategy.ts b/public/docs/_examples/router/ts/app/selective-preload-strategy.ts new file mode 100644 index 0000000000..ebbd40d294 --- /dev/null +++ b/public/docs/_examples/router/ts/app/selective-preload-strategy.ts @@ -0,0 +1,24 @@ +// #docregion +import 'rxjs/add/observable/of'; +import { Injectable } from '@angular/core'; +import { PreloadingStrategy, Route } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +@Injectable() +export class PreloadSelectedModules implements PreloadingStrategy { + preloadedModules: string[] = []; + + preload(route: Route, load: Function): Observable { + if (route.data && route.data['preload']) { + // add the route path to our preloaded module array + this.preloadedModules.push(route.path); + + // log the route path to the console + console.log('Preloaded: ' + route.path); + + return load(); + } else { + return Observable.of(null); + } + } +} diff --git a/public/docs/_examples/router/ts/plnkr.json b/public/docs/_examples/router/ts/plnkr.json index 83b1913f0e..0d7187d384 100644 --- a/public/docs/_examples/router/ts/plnkr.json +++ b/public/docs/_examples/router/ts/plnkr.json @@ -3,7 +3,7 @@ "files":[ "!**/*.d.ts", "!**/*.js", - "!**/*.[1,2,3,4,5,6].*", + "!**/*.[0-9].*", "!app/crisis-list.component.ts", "!app/hero-list.component.ts", "!app/crisis-center/add-crisis.component.ts", diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index 6d471b4702..9d1e1be462 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -59,6 +59,8 @@ include ../_util-fns * providing optional information across routes with [query parameters](#query-parameters) * jumping to anchor elements using a [fragment](#fragment) * loading feature areas [asynchronously](#asynchronous-routing) + * pre-loading feature areas [during navigation](#preloading) + * using a [custom strategy](#custom-preloading) to only pre-load certain features * choosing the "HTML5" or "hash" [URL style](#browser-url-styles) We proceed in phases marked by milestones building from a simple two-pager with placeholder views @@ -620,17 +622,17 @@ h3#router-directives Router Directives a#why-routing-module :marked ### Do you need a _Routing Module_? - - The _Routing Module_ *replaces* the routing configuration in the root or feature module. + + The _Routing Module_ *replaces* the routing configuration in the root or feature module. _Either_ configure routes in the Routing Module _or_ within the module itself but not in both. The Routing Module is a design choice whose value is most obvious when the configuration is complex and includes specialized guard and resolver services. It can seem like overkill when the actual configuration is dead simple. - Some developers skip the Routing Module (e.g., `AppRoutingModule`) when the configuration is simple and + Some developers skip the Routing Module (e.g., `AppRoutingModule`) when the configuration is simple and merge the routing configuration directly into the companion module (e.g., `AppModule`). - + We recommend that you choose one pattern or the other and follow that pattern consistently. Most developers should always implement a Routing Module for the sake of consistency. @@ -1140,8 +1142,8 @@ h3#route-animation Adding animations to the route component :marked Next, we'll use a **host binding** for route animations named *@routeAnimation*. There is nothing special about the choice of the binding name, but since we are controlling route animation, we'll go with `routeAnimation`. - The binding value is set to `true` because we only care about the `*` and `void` states which are - [entering and leaving](../guide/animations.html#example-entering-and-leaving) animation states. + The binding value is set to `true` because we only care about the `:enter` and `:leave` states which are + [entering and leaving](../api/core/index/transition-function.html#transition-aliases-enter-and-leave-) transition aliases. We'll also add some display and positioning bindings for styling. @@ -1150,8 +1152,8 @@ h3#route-animation Adding animations to the route component :marked Now we can build our animation trigger, which we'll call *routeAnimation* to match the binding we previously setup. We'll use the **wildcard state** that matches any animation state our route component is in, along with - two *transitions*. One transition animates the component as it enters the application view (`void => *`), while the other - animates the component as it leaves the application view (`* => void`). + two *transitions*. One transition animates the component as it enters the application view (`:enter`), while the other + animates the component as it leaves the application view (`:leave`). We could add different transitions to different route components depending on our needs. We'll just animate our `HeroDetailComponent` for this milestone. @@ -1903,7 +1905,7 @@ h3#resolve-guard Resolve: pre-fetching component data We need the `Resolve` guard. - ### Preload route information + ### Fetch data before navigating We'll update our `Crisis Detail` route to resolve our Crisis before loading the route, or if the user happens to navigate to an invalid crisis center `:id`, we'll navigate back to our list of existing crises. @@ -1935,7 +1937,7 @@ h3#resolve-guard Resolve: pre-fetching component data We'll add the `CrisisDetailResolve` service to our `CrisisCenterRoutingModule`'s `providers`, so its available to the `Router` during the navigation process. -+makeExcerpt('app/crisis-center/crisis-center-routing.module.ts (resolve)', 'crisis-detail-resolve') ++makeExcerpt('app/crisis-center/crisis-center-routing.module.4.ts (resolve)', 'crisis-detail-resolve') :marked Now that we've added our `Resolve` guard to fetch data before the route loads, we no longer need to do this once we get into our `CrisisDetailComponent`. @@ -1959,7 +1961,7 @@ h3#resolve-guard Resolve: pre-fetching component data `router/ts/app/app.component.ts, router/ts/app/crisis-center/crisis-center-home.component.ts, router/ts/app/crisis-center/crisis-center.component.ts, - router/ts/app/crisis-center/crisis-center-routing.module.ts, + router/ts/app/crisis-center/crisis-center-routing.module.4.ts, router/ts/app/crisis-center/crisis-list.component.ts, router/ts/app/crisis-center/crisis-detail.component.ts, router/ts/app/crisis-center/crisis-detail-resolve.service.ts, @@ -2017,7 +2019,7 @@ a#fragment Since we'll be navigating to our *Admin Dashboard* route after logging in, we'll update it to handle our query parameters and fragment. -+makeExcerpt('app/admin/admin-dashboard.component.ts (v2)', '') ++makeExcerpt('app/admin/admin-dashboard.component.2.ts (v2)', '') :marked *Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components. @@ -2075,7 +2077,7 @@ a#fragment our child routes. +makeTabs( - `router/ts/app/app-routing.module.ts, + `router/ts/app/app-routing.module.5.ts, router/ts/app/admin/admin-routing.module.ts`, 'lazy-load-admin,', `app-routing.module.ts (load children), @@ -2107,7 +2109,7 @@ a#fragment to break our `AdminModule` into a completely separate module. In our `app.module.ts`, we'll remove our `AdminModule` from the `imports` array since we'll be loading it on-demand an we'll remove the imported `AdminModule`. -+makeExcerpt('app/app.module.ts (async admin module)', '') ++makeExcerpt('app/app.module.7.ts (async admin module)', '') h3#can-load-guard CanLoad Guard: guarding against loading of feature modules :marked @@ -2133,7 +2135,111 @@ h3#can-load-guard CanLoad Guard: guarding against loading of feature modu Next, we'll import the `AuthGuard` into our `app-routing.module.ts` and add the `AuthGuard` to the `canLoad` array for our `admin` route. Now our `admin` feature area is only loaded when the proper access has been granted. -+makeExcerpt('app/app-routing.module.ts (can load guard)', 'can-load-guard') ++makeExcerpt('app/app-routing.module.5.ts (can load guard)', 'can-load-guard') + +h3#preloading Pre-Loading: background loading of feature areas +:marked + We've learned how to load modules on-demand, but we can also take advantage of loading feature areas modules in *advance*. The *Router* + supports **pre-loading** of asynchronous feature areas prior to navigation to their respective URL. Pre-loading allows us to to load our initial route + quickly, while other feature modules are loaded in the background. Once we navigate to those areas, they will have already been loaded + as if our they were included in our initial bundle. + + Each time a **successful** navigation happens, the *Router* will look through our configuration for lazy loaded feature areas + and react based on the provided strategy. + + The *Router* supports two pre-loading strategies by default: + + * No pre-loading at all which is the default. Lazy loaded feature areas are still loaded on demand. + * Pre-loading of all lazy loaded feature areas. + + The *Router* also supports [custom preloading strategies](#custom-preloading) to give us control of what we want to pre-load. + + We'll update our *CrisisCenterModule* to be loaded lazily by default and use the `PreloadAllModules` strategy to eagerly + it them up initial navigation. + + +.l-sub-section + :marked + The **PreloadAllModules** strategy does not eagerly load feature areas protected by the [Can Load](#can-load-guard) and this is by design. + The *CanLoad* guard protects against loading feature area assets until authorized to do so. If you want to eagerly load all modules and guard + them against unauthorized access, use the [CanActivate](#can-activate-guard) guard instead. + +:marked + We'll update our route configuration to eagerly load the *CrisisCenterModule*. We follow the same process as we did when we loaded the *AdminModule* asynchronously. + In the *crisis-center-routing.module.ts*, we'll change the *crisis-center* path to an *empty path* route. + + We'll move our redirect and *crisis-center* route to our `AppRoutingModule` routes and use the `loadChildren` string to load the *CrisisCenterModule*. + The redirect is also changed to load the `/heroes` route on initial load. + + Once we're finished, we'll remove the `CrisisCenterModule` from our `AppModule`'s imports. + + Here are our updated modules: + ++makeTabs( + `router/ts/app/app.module.ts, + router/ts/app/app-routing.module.6.ts, + router/ts/app/crisis-center/crisis-center-routing.module.ts + `, + ',preload-v1,', + `app.module.ts, + app-routing.module.ts, + crisis-center-routing.module.ts + `) + +:marked + In order to enable pre-loading of all modules, we'll import the `PreloadAllModules` token from the router package. The second argument in the + `RouterModule.forRoot` method takes an object where we can provide additional configuration options. We'll use the `preloadingStrategy` property + with the `PreloadAllModules` token. This enables the built-in *Router* pre-loader to eagerly load **all** [unguarded](#preload-canload) feature areas that use `loadChildren`. + ++makeExcerpt('app/app-routing.module.6.ts (preload all)', '') + +:marked + Now when we visit `http://localhost:3000`, the `/heroes` route will load in the foreground, while the *CrisisCenterModule* and any other asynchronous feature + modules we could have are _eagerly_ loaded in the background, waiting for us to navigate to them. + + +:marked + ### Custom Pre-Loading Strategy + + Pre-loading all modules works well in some situations, but in some cases we need more control over what gets loaded eagerly. This becomes more clear + as we load our application on a mobile device, or a low bandwidth connection. We may only want to preload certain feature modules based on user metrics + or other data points we gather over time. The *Router* lets us have more control with a **custom** preloading strategy. + + We can define our own strategy the same way the **PreloadAllModules** modules strategy was provided to our *RouterModule.forRoot* configuration object. + + Since we want to take advantage of this, we'll add a custom strategy that _only_ preloads the modules we select. We'll enable the preloading by using the *Route Data*, + which we learned is an object to store arbitrary route data and and [resolve data](#resolve-guard). + + We'll add a custom `preload` boolean to our `crisis-center` route data that we'll use with our custom strategy. To see it in action, we'll add to + the `route.path` to the `preloadedModules` array in our custom strategy service. We'll also log a message + to the console for the preloaded module. + ++makeExcerpt('app/app-routing.module.ts (route data preload)', 'preload-v2') + +:marked + To create our custom strategy we'll need to implement the abstract `PreloadingStrategy` class and the `preload` method. The `preload` method is called for each route + that loads its feature module asynchronously and determines whether to preload it. The `preload` method takes two arguments, the first being the `Route` that provides + the route configuration and a function that preloads the feature module. + + We'll name our strategy **PreloadSelectedModules** since we _only_ want to preload based on certain criteria. Our custom strategy looks for the **`preload`** boolean + value in our `Route Data` and if its true, it calls the `load` function provided by the built-in `Router` pre-loader that eagerly loads feature modules. + ++makeExcerpt('app/selective-preload-strategy.ts (preload selected modules)', '') + +:marked + In order to use our custom preloading strategy, we import it into our `app-routing.module.ts` and replace the `PreloadAllModules` strategy. We also add + the `PreloadSelectedModules` strategy to the `AppRoutingModule` providers array. This allows the *Router* pre-loader to inject our custom strategy. + + To confirm our *CrisisCenterModule* is being pre-loaded, we'll display our `preloadedModules` in the `Admin` dashboard. We already know how to use + an *ngFor* loop, so we'll skip over the details here. Since the `PreloadSelectedModules` is just a service, we can inject it into the `AdminDashboardComponent` + and wire it up to our list. + ++makeExcerpt('app/admin/admin-dashboard.component.ts (preloaded modules)', '') + +:marked + Once our application is loaded to our initial route, the *CrisisCenterModule* is loaded eagerly. We can verify this by logging in to the `Admin` feature area and + noting that the `crisis-center` is listed in the `Preloaded Modules` and logged to the console. We can continue to add feature modules to be selectively loaded eagerly. + .l-main-section