docs(router): Added example of feature module pre-loading (#2595)
* added example of feature module pre-loading * added transition aliases for route animations
This commit is contained in:
parent
3eb7a41602
commit
2f5306f1b6
|
@ -27,7 +27,10 @@ describe('Router', function () {
|
||||||
heroDetailTitle: element(by.css('my-app > ng-component > div > h3')),
|
heroDetailTitle: element(by.css('my-app > ng-component > div > h3')),
|
||||||
|
|
||||||
adminHref: hrefEles.get(2),
|
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) {
|
function crisisCenterEdit(index: number, shouldSave: boolean) {
|
||||||
let page = getPageStruct();
|
let page = getPageStruct();
|
||||||
let crisisEle: ElementFinder;
|
let crisisEle: ElementFinder;
|
||||||
|
|
|
@ -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: `
|
||||||
|
<p>Dashboard</p>
|
||||||
|
|
||||||
|
<p>Session ID: {{ sessionId | async }}</p>
|
||||||
|
<a id="anchor"></a>
|
||||||
|
<p>Token: {{ token | async }}</p>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class AdminDashboardComponent implements OnInit {
|
||||||
|
sessionId: Observable<string>;
|
||||||
|
token: Observable<string>;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { PreloadSelectedModules } from '../selective-preload-strategy';
|
||||||
|
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -11,13 +13,24 @@ import 'rxjs/add/operator/map';
|
||||||
<p>Session ID: {{ sessionId | async }}</p>
|
<p>Session ID: {{ sessionId | async }}</p>
|
||||||
<a id="anchor"></a>
|
<a id="anchor"></a>
|
||||||
<p>Token: {{ token | async }}</p>
|
<p>Token: {{ token | async }}</p>
|
||||||
|
|
||||||
|
Preloaded Modules
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let module of modules">{{ module }}</li>
|
||||||
|
</ul>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class AdminDashboardComponent implements OnInit {
|
export class AdminDashboardComponent implements OnInit {
|
||||||
sessionId: Observable<string>;
|
sessionId: Observable<string>;
|
||||||
token: Observable<string>;
|
token: Observable<string>;
|
||||||
|
modules: string[];
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute) {}
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private preloadStrategy: PreloadSelectedModules
|
||||||
|
) {
|
||||||
|
this.modules = preloadStrategy.preloadedModules;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// Capture the session ID if available
|
// Capture the session ID if available
|
||||||
|
|
|
@ -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 {}
|
|
@ -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 {}
|
|
@ -1,33 +1,43 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion, preload-v1
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
// #docregion import-router
|
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
// #enddocregion import-router
|
|
||||||
|
|
||||||
import { CanDeactivateGuard } from './can-deactivate-guard.service';
|
import { CanDeactivateGuard } from './can-deactivate-guard.service';
|
||||||
// #docregion can-load-guard
|
|
||||||
import { AuthGuard } from './auth-guard.service';
|
import { AuthGuard } from './auth-guard.service';
|
||||||
// #enddocregion can-load-guard
|
import { PreloadSelectedModules } from './selective-preload-strategy';
|
||||||
|
|
||||||
// #docregion lazy-load-admin, can-load-guard
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forRoot([
|
RouterModule.forRoot([
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: 'admin',
|
||||||
loadChildren: 'app/admin/admin.module#AdminModule',
|
loadChildren: 'app/admin/admin.module#AdminModule',
|
||||||
// #enddocregion lazy-load-admin
|
|
||||||
canLoad: [AuthGuard]
|
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: [
|
exports: [
|
||||||
RouterModule
|
RouterModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CanDeactivateGuard
|
CanDeactivateGuard,
|
||||||
|
PreloadSelectedModules
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {}
|
export class AppRoutingModule {}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { AppRoutingModule } from './app-routing.module';
|
||||||
import { HeroesModule } from './heroes/heroes.module';
|
import { HeroesModule } from './heroes/heroes.module';
|
||||||
import { CrisisCenterModule } from './crisis-center/crisis-center.module';
|
import { CrisisCenterModule } from './crisis-center/crisis-center.module';
|
||||||
import { LoginRoutingModule } from './login-routing.module';
|
import { LoginRoutingModule } from './login-routing.module';
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
import { DialogService } from './dialog.service';
|
import { DialogService } from './dialog.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -21,7 +23,8 @@ import { DialogService } from './dialog.service';
|
||||||
AppRoutingModule
|
AppRoutingModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent
|
AppComponent,
|
||||||
|
LoginComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
DialogService
|
DialogService
|
||||||
|
|
|
@ -5,11 +5,9 @@ import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { LoginRoutingModule } from './login-routing.module';
|
|
||||||
|
|
||||||
import { HeroesModule } from './heroes/heroes.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 { LoginComponent } from './login.component';
|
||||||
|
|
||||||
import { DialogService } from './dialog.service';
|
import { DialogService } from './dialog.service';
|
||||||
|
@ -19,7 +17,6 @@ import { DialogService } from './dialog.service';
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HeroesModule,
|
HeroesModule,
|
||||||
CrisisCenterModule,
|
|
||||||
LoginRoutingModule,
|
LoginRoutingModule,
|
||||||
AppRoutingModule
|
AppRoutingModule
|
||||||
],
|
],
|
||||||
|
|
|
@ -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
|
|
@ -16,15 +16,8 @@ import { CrisisDetailResolve } from './crisis-detail-resolve.service';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
// #docregion redirect
|
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
redirectTo: '/crisis-center',
|
|
||||||
pathMatch: 'full'
|
|
||||||
},
|
|
||||||
// #enddocregion redirect
|
|
||||||
{
|
|
||||||
path: 'crisis-center',
|
|
||||||
component: CrisisCenterComponent,
|
component: CrisisCenterComponent,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,14 +35,14 @@ import { DialogService } from '../dialog.service';
|
||||||
transform: 'translateX(0)'
|
transform: 'translateX(0)'
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
transition('void => *', [
|
transition(':enter', [
|
||||||
style({
|
style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateX(-100%)'
|
transform: 'translateX(-100%)'
|
||||||
}),
|
}),
|
||||||
animate('0.2s ease-in')
|
animate('0.2s ease-in')
|
||||||
]),
|
]),
|
||||||
transition('* => void', [
|
transition(':leave', [
|
||||||
animate('0.5s ease-out', style({
|
animate('0.5s ease-out', style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateY(100%)'
|
transform: 'translateY(100%)'
|
||||||
|
|
|
@ -34,14 +34,14 @@ import { DialogService } from '../dialog.service';
|
||||||
transform: 'translateX(0)'
|
transform: 'translateX(0)'
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
transition('void => *', [
|
transition(':enter', [
|
||||||
style({
|
style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateX(-100%)'
|
transform: 'translateX(-100%)'
|
||||||
}),
|
}),
|
||||||
animate('0.2s ease-in')
|
animate('0.2s ease-in')
|
||||||
]),
|
]),
|
||||||
transition('* => void', [
|
transition(':leave', [
|
||||||
animate('0.5s ease-out', style({
|
animate('0.5s ease-out', style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateY(100%)'
|
transform: 'translateY(100%)'
|
||||||
|
|
|
@ -34,14 +34,14 @@ import { Hero, HeroService } from './hero.service';
|
||||||
transform: 'translateX(0)'
|
transform: 'translateX(0)'
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
transition('void => *', [
|
transition(':enter', [
|
||||||
style({
|
style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateX(-100%)'
|
transform: 'translateX(-100%)'
|
||||||
}),
|
}),
|
||||||
animate('0.2s ease-in')
|
animate('0.2s ease-in')
|
||||||
]),
|
]),
|
||||||
transition('* => void', [
|
transition(':leave', [
|
||||||
animate('0.5s ease-out', style({
|
animate('0.5s ease-out', style({
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transform: 'translateY(100%)'
|
transform: 'translateY(100%)'
|
||||||
|
|
|
@ -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<any> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
"files":[
|
"files":[
|
||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js",
|
"!**/*.js",
|
||||||
"!**/*.[1,2,3,4,5,6].*",
|
"!**/*.[0-9].*",
|
||||||
"!app/crisis-list.component.ts",
|
"!app/crisis-list.component.ts",
|
||||||
"!app/hero-list.component.ts",
|
"!app/hero-list.component.ts",
|
||||||
"!app/crisis-center/add-crisis.component.ts",
|
"!app/crisis-center/add-crisis.component.ts",
|
||||||
|
|
|
@ -59,6 +59,8 @@ include ../_util-fns
|
||||||
* providing optional information across routes with [query parameters](#query-parameters)
|
* providing optional information across routes with [query parameters](#query-parameters)
|
||||||
* jumping to anchor elements using a [fragment](#fragment)
|
* jumping to anchor elements using a [fragment](#fragment)
|
||||||
* loading feature areas [asynchronously](#asynchronous-routing)
|
* 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)
|
* 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
|
We proceed in phases marked by milestones building from a simple two-pager with placeholder views
|
||||||
|
@ -1140,8 +1142,8 @@ h3#route-animation Adding animations to the route component
|
||||||
:marked
|
:marked
|
||||||
Next, we'll use a **host binding** for route animations named *@routeAnimation*. There is nothing special
|
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`.
|
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
|
The binding value is set to `true` because we only care about the `:enter` and `:leave` states which are
|
||||||
[entering and leaving](../guide/animations.html#example-entering-and-leaving) animation states.
|
[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.
|
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
|
:marked
|
||||||
Now we can build our animation trigger, which we'll call *routeAnimation* to match the binding we previously
|
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
|
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
|
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 (`* => void`).
|
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.
|
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 <i>Resolve</i>: pre-fetching component data
|
||||||
|
|
||||||
We need the `Resolve` guard.
|
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
|
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.
|
navigate to an invalid crisis center `:id`, we'll navigate back to our list of existing crises.
|
||||||
|
@ -1935,7 +1937,7 @@ h3#resolve-guard <i>Resolve</i>: 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.
|
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
|
: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`.
|
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 <i>Resolve</i>: pre-fetching component data
|
||||||
`router/ts/app/app.component.ts,
|
`router/ts/app/app.component.ts,
|
||||||
router/ts/app/crisis-center/crisis-center-home.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.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-list.component.ts,
|
||||||
router/ts/app/crisis-center/crisis-detail.component.ts,
|
router/ts/app/crisis-center/crisis-detail.component.ts,
|
||||||
router/ts/app/crisis-center/crisis-detail-resolve.service.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
|
Since we'll be navigating to our *Admin Dashboard* route after logging in, we'll update it to handle our
|
||||||
query parameters and fragment.
|
query parameters and fragment.
|
||||||
|
|
||||||
+makeExcerpt('app/admin/admin-dashboard.component.ts (v2)', '')
|
+makeExcerpt('app/admin/admin-dashboard.component.2.ts (v2)', '')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
*Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components.
|
*Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components.
|
||||||
|
@ -2075,7 +2077,7 @@ a#fragment
|
||||||
our child routes.
|
our child routes.
|
||||||
|
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`router/ts/app/app-routing.module.ts,
|
`router/ts/app/app-routing.module.5.ts,
|
||||||
router/ts/app/admin/admin-routing.module.ts`,
|
router/ts/app/admin/admin-routing.module.ts`,
|
||||||
'lazy-load-admin,',
|
'lazy-load-admin,',
|
||||||
`app-routing.module.ts (load children),
|
`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
|
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`.
|
`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 <i>CanLoad Guard</i>: guarding against loading of feature modules
|
h3#can-load-guard <i>CanLoad Guard</i>: guarding against loading of feature modules
|
||||||
:marked
|
:marked
|
||||||
|
@ -2133,7 +2135,111 @@ h3#can-load-guard <i>CanLoad Guard</i>: 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
|
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.
|
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 <i>Pre-Loading</i>: 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.
|
||||||
|
|
||||||
|
<a id="preload-canload"></a>
|
||||||
|
.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.
|
||||||
|
|
||||||
|
<a id="custom-preloading"></a>
|
||||||
|
: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.
|
||||||
|
|
||||||
|
|
||||||
<a id="final-app"></a>
|
<a id="final-app"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
|
Loading…
Reference in New Issue