diff --git a/aio/content/examples/ngmodule/src/app/crisis/crisis-detail.component.ts b/aio/content/examples/ngmodule/src/app/crisis/crisis-detail.component.ts index 9749029d62..30a1d0e7bf 100644 --- a/aio/content/examples/ngmodule/src/app/crisis/crisis-detail.component.ts +++ b/aio/content/examples/ngmodule/src/app/crisis/crisis-detail.component.ts @@ -14,6 +14,6 @@ export class CrisisDetailComponent implements OnInit { constructor(private route: ActivatedRoute) { } ngOnInit() { - this.id = parseInt(this.route.snapshot.params['id'], 10); + this.id = parseInt(this.route.snapshot.paramMap.get('id'), 10); } } diff --git a/aio/content/examples/ngmodule/src/app/hero/hero-detail.component.ts b/aio/content/examples/ngmodule/src/app/hero/hero-detail.component.ts index 1478ad350c..3685cbfde5 100644 --- a/aio/content/examples/ngmodule/src/app/hero/hero-detail.component.ts +++ b/aio/content/examples/ngmodule/src/app/hero/hero-detail.component.ts @@ -25,7 +25,7 @@ export class HeroDetailComponent implements OnInit { private heroService: HeroService) { } ngOnInit() { - let id = parseInt(this.route.snapshot.params['id'], 10); + let id = parseInt(this.route.snapshot.paramMap.get('id'), 10); this.heroService.getHero(id).then(hero => this.hero = hero); } } diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts b/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts index 8c8e481643..ca9ec37bb9 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard.component.2.ts @@ -22,8 +22,8 @@ export class AdminDashboardComponent implements OnInit { ngOnInit() { // Capture the session ID if available this.sessionId = this.route - .queryParams - .map(params => params['session_id'] || 'None'); + .queryParamMap + .map(params => params.get('session_id') || 'None'); // Capture the fragment if available this.token = this.route diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts b/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts index b3fc839616..1602319c08 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard.component.ts @@ -36,8 +36,8 @@ export class AdminDashboardComponent implements OnInit { ngOnInit() { // Capture the session ID if available this.sessionId = this.route - .queryParams - .map(params => params['session_id'] || 'None'); + .queryParamMap + .map(params => params.get('session_id') || 'None'); // Capture the fragment if available this.token = this.route diff --git a/aio/content/examples/router/src/app/app-routing.module.1.ts b/aio/content/examples/router/src/app/app-routing.module.1.ts index 8146e54671..436291f499 100644 --- a/aio/content/examples/router/src/app/app-routing.module.1.ts +++ b/aio/content/examples/router/src/app/app-routing.module.1.ts @@ -17,7 +17,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], exports: [ RouterModule diff --git a/aio/content/examples/router/src/app/app-routing.module.2.ts b/aio/content/examples/router/src/app/app-routing.module.2.ts index 42ac84a481..c4df89d7f8 100644 --- a/aio/content/examples/router/src/app/app-routing.module.2.ts +++ b/aio/content/examples/router/src/app/app-routing.module.2.ts @@ -15,7 +15,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], exports: [ RouterModule diff --git a/aio/content/examples/router/src/app/app-routing.module.3.ts b/aio/content/examples/router/src/app/app-routing.module.3.ts index 538ff9aafc..354ca6e740 100644 --- a/aio/content/examples/router/src/app/app-routing.module.3.ts +++ b/aio/content/examples/router/src/app/app-routing.module.3.ts @@ -22,7 +22,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], exports: [ RouterModule diff --git a/aio/content/examples/router/src/app/app-routing.module.4.ts b/aio/content/examples/router/src/app/app-routing.module.4.ts index 6835d24a85..9c0d7bb928 100644 --- a/aio/content/examples/router/src/app/app-routing.module.4.ts +++ b/aio/content/examples/router/src/app/app-routing.module.4.ts @@ -18,7 +18,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], exports: [ RouterModule diff --git a/aio/content/examples/router/src/app/app-routing.module.5.ts b/aio/content/examples/router/src/app/app-routing.module.5.ts index 2badf7f593..a12bd2cc7e 100644 --- a/aio/content/examples/router/src/app/app-routing.module.5.ts +++ b/aio/content/examples/router/src/app/app-routing.module.5.ts @@ -33,7 +33,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], exports: [ RouterModule diff --git a/aio/content/examples/router/src/app/app-routing.module.6.ts b/aio/content/examples/router/src/app/app-routing.module.6.ts index df2c8c097d..83a6ab3521 100644 --- a/aio/content/examples/router/src/app/app-routing.module.6.ts +++ b/aio/content/examples/router/src/app/app-routing.module.6.ts @@ -37,9 +37,12 @@ const appRoutes: Routes = [ imports: [ // #docregion forRoot RouterModule.forRoot( - appRoutes + appRoutes, // #enddocregion preload-v1 - , { preloadingStrategy: PreloadAllModules } + { + enableTracing: true, // <-- debugging purposes only + preloadingStrategy: PreloadAllModules + } // #docregion preload-v1 ) // #enddocregion forRoot diff --git a/aio/content/examples/router/src/app/app-routing.module.ts b/aio/content/examples/router/src/app/app-routing.module.ts index cc01ced890..17ba610edc 100644 --- a/aio/content/examples/router/src/app/app-routing.module.ts +++ b/aio/content/examples/router/src/app/app-routing.module.ts @@ -36,7 +36,11 @@ const appRoutes: Routes = [ imports: [ RouterModule.forRoot( appRoutes, - { preloadingStrategy: SelectivePreloadingStrategy } + { + enableTracing: true, // <-- debugging purposes only + preloadingStrategy: SelectivePreloadingStrategy, + + } ) ], exports: [ diff --git a/aio/content/examples/router/src/app/app.module.0.ts b/aio/content/examples/router/src/app/app.module.0.ts index a195dbdd7a..5d14073b1b 100644 --- a/aio/content/examples/router/src/app/app.module.0.ts +++ b/aio/content/examples/router/src/app/app.module.0.ts @@ -26,7 +26,10 @@ const appRoutes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) // other imports here ], // #enddocregion diff --git a/aio/content/examples/router/src/app/app.module.1.ts b/aio/content/examples/router/src/app/app.module.1.ts index 32f93b8f79..e83e299a16 100644 --- a/aio/content/examples/router/src/app/app.module.1.ts +++ b/aio/content/examples/router/src/app/app.module.1.ts @@ -33,7 +33,10 @@ const appRoutes: Routes = [ imports: [ BrowserModule, FormsModule, - RouterModule.forRoot(appRoutes) + RouterModule.forRoot( + appRoutes, + { enableTracing: true } // <-- debugging purposes only + ) ], declarations: [ AppComponent, diff --git a/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts b/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts index 0b7c8247cf..78520372a9 100644 --- a/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts +++ b/aio/content/examples/router/src/app/can-deactivate-guard.service.1.ts @@ -15,7 +15,7 @@ export class CanDeactivateGuard implements CanDeactivate state: RouterStateSnapshot ): Promise | boolean { // Get the Crisis Center ID - console.log(route.params['id']); + console.log(route.paramMap.get('id')); // Get the current URL console.log(state.url); diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts index a71d485c02..0edc35bc6e 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-home.component.ts @@ -1,13 +1,9 @@ // #docregion -// #docplaster import { Component } from '@angular/core'; -// #docregion minus-imports @Component({ template: `

Welcome to the Crisis Center

` }) export class CrisisCenterHomeComponent { } -// #enddocregion minus-imports -// #enddocregion diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts index e646f467d1..8ef60e68a1 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts @@ -31,6 +31,7 @@ const crisisCenterRoutes: Routes = [ ] } ]; +// #enddocregion routes @NgModule({ imports: [ diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts index 31d1790f45..c7d7fe412d 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center.component.ts @@ -1,8 +1,6 @@ // #docregion -// #docplaster import { Component } from '@angular/core'; -// #docregion minus-imports @Component({ template: `

CRISIS CENTER

@@ -10,5 +8,3 @@ import { Component } from '@angular/core'; ` }) export class CrisisCenterComponent { } -// #enddocregion minus-imports -// #enddocregion diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts index 94b4cd33e7..e6782166ef 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts @@ -10,7 +10,7 @@ export class CrisisDetailResolver implements Resolve { constructor(private cs: CrisisService, private router: Router) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { - let id = route.params['id']; + let id = route.paramMap.get('id'); return this.cs.getCrisis(id).then(crisis => { if (crisis) { diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts index d6fa27f629..6f25040a27 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail.component.1.ts @@ -2,7 +2,7 @@ // #docregion import 'rxjs/add/operator/switchMap'; import { Component, OnInit, HostBinding } from '@angular/core'; -import { ActivatedRoute, Router, Params } from '@angular/router'; +import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { slideInDownAnimation } from '../animations'; import { Crisis, CrisisService } from './crisis.service'; @@ -44,8 +44,8 @@ export class CrisisDetailComponent implements OnInit { // #docregion ngOnInit ngOnInit() { - this.route.params - .switchMap((params: Params) => this.service.getCrisis(params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => this.service.getCrisis(params.get('id'))) .subscribe((crisis: Crisis) => { if (crisis) { this.editName = crisis.name; diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts index 0000dde082..50d27afc48 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list.component.1.ts @@ -1,7 +1,7 @@ import 'rxjs/add/operator/do'; import 'rxjs/add/operator/switchMap'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router, Params } from '@angular/router'; +import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { Crisis, CrisisService } from './crisis.service'; import { Observable } from 'rxjs/Observable'; @@ -31,9 +31,9 @@ export class CrisisListComponent implements OnInit { ) {} ngOnInit() { - this.crises = this.route.params - .switchMap((params: Params) => { - this.selectedId = +params['id']; + this.crises = this.route.paramMap + .switchMap((params: ParamMap) => { + this.selectedId = +params.get('id'); return this.service.getCrises(); }); } diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts index 4498a55c0f..b26d9e33aa 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list.component.ts @@ -1,7 +1,7 @@ // #docregion import 'rxjs/add/operator/switchMap'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router, Params } from '@angular/router'; +import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { Observable } from 'rxjs/Observable'; @@ -38,9 +38,9 @@ export class CrisisListComponent implements OnInit { } ngOnInit() { - this.crises = this.route.params - .switchMap((params: Params) => { - this.selectedId = +params['id']; + this.crises = this.route.paramMap + .switchMap((params: ParamMap) => { + this.selectedId = +params.get('id'); return this.service.getCrises(); }); } diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts b/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts index 93f0efaf0b..e5ad0b12fe 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail.component.1.ts @@ -5,7 +5,7 @@ import 'rxjs/add/operator/switchMap'; // #enddocregion rxjs-operator-import import { Component, OnInit } from '@angular/core'; // #docregion imports -import { Router, ActivatedRoute, Params } from '@angular/router'; +import { Router, ActivatedRoute, ParamMap } from '@angular/router'; // #enddocregion imports import { Hero, HeroService } from './hero.service'; @@ -40,9 +40,9 @@ export class HeroDetailComponent implements OnInit { // #docregion ngOnInit ngOnInit() { - this.route.params - // (+) converts string 'id' to a number - .switchMap((params: Params) => this.service.getHero(+params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => + this.service.getHero(params.get('id'))) .subscribe((hero: Hero) => this.hero = hero); } // #enddocregion ngOnInit diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts b/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts index c3b69be965..de5633344d 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail.component.2.ts @@ -33,8 +33,7 @@ export class HeroDetailComponent implements OnInit { // #docregion snapshot ngOnInit() { - // (+) converts string 'id' to a number - let id = +this.route.snapshot.params['id']; + let id = this.route.snapshot.paramMap.get('id'); this.service.getHero(id) .then((hero: Hero) => this.hero = hero); diff --git a/aio/content/examples/router/src/app/heroes/hero-detail.component.ts b/aio/content/examples/router/src/app/heroes/hero-detail.component.ts index 8135d37d32..09dfdce1c8 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail.component.ts @@ -4,7 +4,7 @@ import 'rxjs/add/operator/switchMap'; // #enddocregion rxjs-operator-import import { Component, OnInit, HostBinding } from '@angular/core'; -import { Router, ActivatedRoute, Params } from '@angular/router'; +import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { slideInDownAnimation } from '../animations'; @@ -47,9 +47,9 @@ export class HeroDetailComponent implements OnInit { // #docregion ngOnInit ngOnInit() { - this.route.params - // (+) converts string 'id' to a number - .switchMap((params: Params) => this.service.getHero(+params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => + this.service.getHero(params.get('id'))) .subscribe((hero: Hero) => this.hero = hero); } // #enddocregion ngOnInit diff --git a/aio/content/examples/router/src/app/heroes/hero-list.component.ts b/aio/content/examples/router/src/app/heroes/hero-list.component.ts index c7dcc3877e..68266337ac 100644 --- a/aio/content/examples/router/src/app/heroes/hero-list.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-list.component.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs/Observable'; // #enddocregion rxjs-imports import { Component, OnInit } from '@angular/core'; // #docregion import-router -import { Router, ActivatedRoute, Params } from '@angular/router'; +import { Router, ActivatedRoute, ParamMap } from '@angular/router'; // #enddocregion import-router import { Hero, HeroService } from './hero.service'; @@ -41,9 +41,10 @@ export class HeroListComponent implements OnInit { ) {} ngOnInit() { - this.heroes = this.route.params - .switchMap((params: Params) => { - this.selectedId = +params['id']; + this.heroes = this.route.paramMap + .switchMap((params: ParamMap) => { + // (+) before `params.get()` turns the string into a number + this.selectedId = +params.get('id'); return this.service.getHeroes(); }); } diff --git a/aio/content/examples/router/src/app/heroes/hero.service.ts b/aio/content/examples/router/src/app/heroes/hero.service.ts index 6e4e7bee60..b689a3586a 100644 --- a/aio/content/examples/router/src/app/heroes/hero.service.ts +++ b/aio/content/examples/router/src/app/heroes/hero.service.ts @@ -22,6 +22,7 @@ export class HeroService { getHero(id: number | string) { return heroesPromise + // (+) before `id` turns the string into a number .then(heroes => heroes.find(hero => hero.id === +id)); } } diff --git a/aio/content/examples/router/src/app/login.component.ts b/aio/content/examples/router/src/app/login.component.ts index 41c88f4068..1a6fae162f 100644 --- a/aio/content/examples/router/src/app/login.component.ts +++ b/aio/content/examples/router/src/app/login.component.ts @@ -38,7 +38,7 @@ export class LoginComponent { // Set our navigation extras object // that passes on our global query params and fragment let navigationExtras: NavigationExtras = { - preserveQueryParams: true, + queryParamsHandling: 'preserve', preserveFragment: true }; diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts index 422dae6f77..9dffe6f9de 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.no-testbed.spec.ts @@ -15,7 +15,7 @@ describe('HeroDetailComponent - no TestBed', () => { beforeEach((done: any) => { expectedHero = new Hero(42, 'Bubba'); activatedRoute = new ActivatedRouteStub(); - activatedRoute.testParams = { id: expectedHero.id }; + activatedRoute.testParamMap = { id: expectedHero.id }; router = jasmine.createSpyObj('router', ['navigate']); diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts index 80b6450ac5..7870f10308 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts @@ -53,7 +53,7 @@ function overrideSetup() { // #enddocregion hds-spy // the `id` value is irrelevant because ignored by service stub - beforeEach(() => activatedRoute.testParams = { id: 99999 } ); + beforeEach(() => activatedRoute.testParamMap = { id: 99999 } ); // #docregion setup-override beforeEach( async(() => { @@ -158,7 +158,7 @@ function heroModuleSetup() { beforeEach( async(() => { expectedHero = firstHero; - activatedRoute.testParams = { id: expectedHero.id }; + activatedRoute.testParamMap = { id: expectedHero.id }; createComponent(); })); @@ -229,7 +229,7 @@ function heroModuleSetup() { // #docregion route-bad-id describe('when navigate to non-existant hero id', () => { beforeEach( async(() => { - activatedRoute.testParams = { id: 99999 }; + activatedRoute.testParamMap = { id: 99999 }; createComponent(); })); @@ -279,7 +279,7 @@ function formsModuleSetup() { it('should display 1st hero\'s name', fakeAsync(() => { const expectedHero = firstHero; - activatedRoute.testParams = { id: expectedHero.id }; + activatedRoute.testParamMap = { id: expectedHero.id }; createComponent().then(() => { expect(page.nameDisplay.textContent).toBe(expectedHero.name); }); @@ -307,7 +307,7 @@ function sharedModuleSetup() { it('should display 1st hero\'s name', fakeAsync(() => { const expectedHero = firstHero; - activatedRoute.testParams = { id: expectedHero.id }; + activatedRoute.testParamMap = { id: expectedHero.id }; createComponent().then(() => { expect(page.nameDisplay.textContent).toBe(expectedHero.name); }); diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.ts index 25f13b0cd5..d989badbf3 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.ts @@ -29,7 +29,7 @@ export class HeroDetailComponent implements OnInit { // #docregion ng-on-init ngOnInit(): void { // get hero when `id` param changes - this.route.params.subscribe(p => this.getHero(p && p['id'])); + this.route.paramMap.subscribe(p => this.getHero(p.has('id') && p.get('id'))); } // #enddocregion ng-on-init diff --git a/aio/content/examples/testing/src/testing/router-stubs.ts b/aio/content/examples/testing/src/testing/router-stubs.ts index 75a2858f65..880302c7be 100644 --- a/aio/content/examples/testing/src/testing/router-stubs.ts +++ b/aio/content/examples/testing/src/testing/router-stubs.ts @@ -30,28 +30,29 @@ export class RouterStub { } -// Only implements params and part of snapshot.params +// Only implements params and part of snapshot.paramMap // #docregion activated-route-stub import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { convertToParamMap, ParamMap } from '@angular/router'; @Injectable() export class ActivatedRouteStub { - // ActivatedRoute.params is Observable - private subject = new BehaviorSubject(this.testParams); - params = this.subject.asObservable(); + // ActivatedRoute.paramMap is Observable + private subject = new BehaviorSubject(convertToParamMap(this.testParamMap)); + paramMap = this.subject.asObservable(); // Test parameters - private _testParams: {}; - get testParams() { return this._testParams; } - set testParams(params: {}) { - this._testParams = params; - this.subject.next(params); + private _testParamMap: ParamMap; + get testParamMap() { return this._testParamMap; } + set testParamMap(params: {}) { + this._testParamMap = convertToParamMap(params); + this.subject.next(this._testParamMap); } - // ActivatedRoute.snapshot.params + // ActivatedRoute.snapshot.paramMap get snapshot() { - return { params: this.testParams }; + return { paramMap: this.testParamMap }; } } // #enddocregion activated-route-stub diff --git a/aio/content/examples/toh-pt5/src/app/hero-detail.component.1.ts b/aio/content/examples/toh-pt5/src/app/hero-detail.component.1.ts index 2f5081bdce..2895161fee 100644 --- a/aio/content/examples/toh-pt5/src/app/hero-detail.component.1.ts +++ b/aio/content/examples/toh-pt5/src/app/hero-detail.component.1.ts @@ -5,7 +5,7 @@ // #docregion added-imports // Keep the Input import for now, you'll remove it later: import { Component, Input, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, ParamMap } from '@angular/router'; import { Location } from '@angular/common'; import { HeroService } from './hero.service'; @@ -17,7 +17,7 @@ import { Hero } from './hero'; @Component({}) export class HeroDetailComponent implements OnInit { @Input() hero: Hero; - bogus: Params; + bogus: ParamMap; constructor( private heroService: HeroService, diff --git a/aio/content/examples/toh-pt5/src/app/hero-detail.component.ts b/aio/content/examples/toh-pt5/src/app/hero-detail.component.ts index 222b6705d7..01f3a4c2db 100644 --- a/aio/content/examples/toh-pt5/src/app/hero-detail.component.ts +++ b/aio/content/examples/toh-pt5/src/app/hero-detail.component.ts @@ -2,9 +2,9 @@ // #docregion , v2, rxjs-import import 'rxjs/add/operator/switchMap'; // #enddocregion rxjs-import -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; -import { Location } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { Location } from '@angular/common'; import { Hero } from './hero'; import { HeroService } from './hero.service'; @@ -32,8 +32,8 @@ export class HeroDetailComponent implements OnInit { // #docregion ngOnInit ngOnInit(): void { - this.route.params - .switchMap((params: Params) => this.heroService.getHero(+params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id'))) .subscribe(hero => this.hero = hero); } // #enddocregion ngOnInit diff --git a/aio/content/examples/toh-pt6/src/app/hero-detail.component.ts b/aio/content/examples/toh-pt6/src/app/hero-detail.component.ts index 6224f10ac1..e0222aded7 100644 --- a/aio/content/examples/toh-pt6/src/app/hero-detail.component.ts +++ b/aio/content/examples/toh-pt6/src/app/hero-detail.component.ts @@ -1,8 +1,8 @@ // #docregion import 'rxjs/add/operator/switchMap'; -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; -import { Location } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { Location } from '@angular/common'; import { Hero } from './hero'; import { HeroService } from './hero.service'; @@ -22,8 +22,8 @@ export class HeroDetailComponent implements OnInit { ) {} ngOnInit(): void { - this.route.params - .switchMap((params: Params) => this.heroService.getHero(+params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id'))) .subscribe(hero => this.hero = hero); } diff --git a/aio/content/examples/universal/src/app/hero-detail.component.ts b/aio/content/examples/universal/src/app/hero-detail.component.ts index b3bbbd2f3d..676c4c21b0 100644 --- a/aio/content/examples/universal/src/app/hero-detail.component.ts +++ b/aio/content/examples/universal/src/app/hero-detail.component.ts @@ -1,7 +1,7 @@ // #docregion import 'rxjs/add/operator/switchMap'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, ParamMap } from '@angular/router'; import { Location } from '@angular/common'; import { Hero } from './hero'; @@ -22,8 +22,8 @@ export class HeroDetailComponent implements OnInit { ) {} ngOnInit(): void { - this.route.params - .switchMap((params: Params) => this.heroService.getHero(+params['id'])) + this.route.paramMap + .switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id'))) .subscribe(hero => this.hero = hero); } diff --git a/aio/content/examples/upgrade-phonecat-3-final/app/phone-detail/phone-detail.component.ts b/aio/content/examples/upgrade-phonecat-3-final/app/phone-detail/phone-detail.component.ts index dd47f746f6..95aeca20e0 100644 --- a/aio/content/examples/upgrade-phonecat-3-final/app/phone-detail/phone-detail.component.ts +++ b/aio/content/examples/upgrade-phonecat-3-final/app/phone-detail/phone-detail.component.ts @@ -14,7 +14,7 @@ export class PhoneDetailComponent { mainImageUrl: string; constructor(activatedRoute: ActivatedRoute, phone: Phone) { - phone.get(activatedRoute.snapshot.params['phoneId']) + phone.get(activatedRoute.snapshot.paramMap.get('phoneId')) .subscribe((p: PhoneData) => { this.phone = p; this.setImage(p.images[0]); diff --git a/aio/content/guide/router.md b/aio/content/guide/router.md index 63dec904eb..6e8e71d2a5 100644 --- a/aio/content/guide/router.md +++ b/aio/content/guide/router.md @@ -6,6 +6,10 @@ as users perform application tasks. This guide covers the router's primary features, illustrating them through the evolution of a small application that you can run live in the browser. + + ## Overview @@ -75,7 +79,7 @@ Import what you need from it as you would from any other Angular package. -You'll learn about more options in the [details below](guide/router#browser-url-styles). +You'll learn about more options in the [details below](#browser-url-styles). @@ -121,7 +125,7 @@ You'll learn more about route parameters later in this guide. The `data` property in the third route is a place to store arbitrary data associated with this specific route. The data property is accessible within each activated route. Use it to store items such as page titles, breadcrumb text, and other read-only, _static_ data. -You'll use the [resolve guard](guide/router#resolve-guard) to retrieve _dynamic_ data later in the guide. +You'll use the [resolve guard](#resolve-guard) to retrieve _dynamic_ data later in the guide. The **empty path** in the fourth route represents the default path for the application, the place to go when the path in the URL is empty, as it typically is at the start. @@ -137,6 +141,7 @@ In the configuration above, routes with a static path are listed first, followed that matches the default route. The wildcard route comes last because it matches _every URL_ and should be selected _only_ if no other routes are matched first. +If you need to see what events are happening during the navigation lifecycle, there is the **enableTracing** option as part of the router's default configuration. This outputs each router event that took place during each navigation lifecycle to the browser console. This should only be used for _debugging_ purposes. You set the `enableTracing: true` option in the object passed as the second argument to the `RouterModule.forRoot()` method. {@a basics-router-outlet} @@ -199,6 +204,103 @@ application using the `Router` service and the `routerState` property. Each `ActivatedRoute` in the `RouterState` provides methods to traverse up and down the route tree to get information from parent, child and sibling routes. +### Router events + +During each navigation, the `Router` emits navigation events through the `Router.events` property. These events range from when the navigation starts and ends to many points in between. The full list of navigation events is displayed in the table below. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Router Event + + Description +
+ NavigationStart + + + An [event](api/router/NavigationStart) triggered when navigation starts. + +
+ RoutesRecognized + + + An [event](api/router/RoutesRecognized) triggered when the Router parses the URL and the routes are recognized. + +
+ RouteConfigLoadStart + + + An [event](api/router/RouteConfigLoadStart) triggered before the `Router` + [lazy loads](#asynchronous-routing) a route configuration. + +
+ RouteConfigLoadStart + + + An [event](api/router/RouteConfigLoadStart) triggered after a route has been lazy loaded. + +
+ NavigationEnd + + + An [event](api/router/NavigationEnd) triggered when navigation ends successfully. + +
+ NavigationCancel + + + An [event](api/router/NavigationCancel) triggered when navigation is canceled. + This is due to a [Route Guard](#guards) returning false during navigation. + +
+ NavigationError + + + An [event](api/router/NavigationError) triggered when navigation fails due to an unexpected error. + +
+ +These events are logged to the console when the `enableTracing` option is enabled also. Since the events are provided as an `Observable`, you can `filter()` for events of interest and `subscribe()` to them to make decisions based on the sequence of events in the navigation process. + {@a basics-summary} @@ -211,12 +313,6 @@ It has `RouterLink`s that users can click to navigate via the router. Here are the key `Router` terms and their meanings: - - - - @@ -369,7 +465,6 @@ Here are the key `Router` terms and their meanings: @@ -523,7 +618,7 @@ Modern HTML5 browsers were the first to support `pushState` which is why many pe HTML5 style navigation is the router default. -In the [LocationStrategy and browser URL styles](guide/router#browser-url-styles) Appendix, +In the [LocationStrategy and browser URL styles](#browser-url-styles) Appendix, learn why HTML5 style is preferred, how to adjust its behavior, and how to switch to the older hash (#) style, if necessary. @@ -634,7 +729,7 @@ Once the application is bootstrapped, the `Router` performs the initial navigati Adding the configured `RouterModule` to the `AppModule` is sufficient for simple route configurations. As the application grows, you'll want to refactor the routing configuration into a separate file -and create a **[Routing Module](guide/router#routing-module)**, a special type of `Service Module` dedicated to the purpose +and create a **[Routing Module](#routing-module)**, a special type of `Service Module` dedicated to the purpose of routing in feature modules. @@ -715,7 +810,7 @@ takes a single value bound to the `[fragment]` input binding. -Learn about the how you can also use the _link parameters array_ in the [appendix below](guide/router#link-parameters-array). +Learn about the how you can also use the _link parameters array_ in the [appendix below](#link-parameters-array). @@ -732,7 +827,7 @@ the `RouterLinkActive` directive that look like `routerLinkActive="..."`. The template expression to the right of the equals (=) contains a space-delimited string of CSS classes that the Router will add when this link is active (and remove when the link is inactive). -You can also set the `RouterLinkActive` directive to a string of classes such as `[routerLinkActive]="active fluffy"` +You can also set the `RouterLinkActive` directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` or bind it to a component property that returns such a string. The `RouterLinkActive` directive toggles css classes for active `RouterLink`s based on the current `RouterState`. @@ -769,14 +864,14 @@ Any other URL causes the router to throw an error and crash the app. Add a **wildcard** route to intercept invalid URLs and handle them gracefully. A _wildcard_ route has a path consisting of two asterisks. It matches _every_ URL. The router will select _this_ route if it can't match a route earlier in the configuration. -A wildcard route can navigate to a custom "404 Not Found" component or [redirect](guide/router#redirect) to an existing route. +A wildcard route can navigate to a custom "404 Not Found" component or [redirect](#redirect) to an existing route.
-The router selects the route with a [_first match wins_](guide/router#example-config) strategy. +The router selects the route with a [_first match wins_](#example-config) strategy. Wildcard routes are the least specific routes in the route configuration. Be sure it is the _last_ route in the configuration. @@ -825,24 +920,19 @@ The browser address bar continues to point to the invalid URL. When the application launches, the initial URL in the browser bar is something like: - localhost:3000 - +That doesn't match any of the concrete configured routes which means +the router falls through to the wildcard route and displays the `PageNotFoundComponent`. - -That doesn't match any of the configured routes which means that the application won't display any component when it's launched. -The user must click one of the links to trigger a navigation and display a component. - -It would be nicer if the application had a **default route** that displayed the list of heroes immediately, -just as it will when the user clicks the "Heroes" link or pastes `localhost:3000/heroes` into the address bar. - +The application needs a **default route** to a valid page. +The default page for this app is the list of heroes. +The app should navigate there as if the user clicked the "Heroes" link or pasted `localhost:3000/heroes` into the address bar. {@a redirect} - ### Redirecting routes The preferred solution is to add a `redirect` route that translates the initial relative URL (`''`) @@ -853,7 +943,6 @@ It's just above the wildcard route in the following excerpt showing the complete - @@ -1085,8 +1174,8 @@ then replacing `RouterModule.forRoot` in the `imports` array with the `AppRoutin -Later in this guide you will create [multiple routing modules](guide/router#hero-routing-module) and discover that -you must import those routing modules [in the correct order](guide/router#routing-module-order). +Later in this guide you will create [multiple routing modules](#hero-routing-module) and discover that +you must import those routing modules [in the correct order](#routing-module-order).
@@ -1377,7 +1466,7 @@ The order of route configuration matters. The router accepts the first route that matches a navigation request path. When all routes were in one `AppRoutingModule`, -you put the default and [wildcard](guide/router#wildcard) routes last, after the `/heroes` route, +you put the default and [wildcard](#wildcard) routes last, after the `/heroes` route, so that the router had a chance to match a URL to the `/heroes` route _before_ hitting the wildcard route and navigating to "Page not found". @@ -1398,7 +1487,7 @@ will intercept the attempt to navigate to a hero route. Reverse the routing modules and see for yourself that a click of the heroes link results in "Page not found". Learn about inspecting the runtime router configuration -[below](guide/router#inspect-config "Inspect the router config"). +[below](#inspect-config "Inspect the router config"). @@ -1551,37 +1640,143 @@ The route path and parameters are available through an injected router service c [ActivatedRoute](api/router/ActivatedRoute). It has a great deal of useful information including: +
An Angular component with a RouterOutlet that displays views based on router navigations. -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Property + + Description +
+ url + + + An `Observable` of the route path(s), represented as an array of strings for each part of the route path. + +
+ data + + + An `Observable` that contains the `data` object provided for the route. Also contains any resolved values from the [resolve guard](#resolve-guard). + +
+ paramMap + + + An `Observable` that contains a [map](api/router/ParamMap) of the required and [optional parameters](#optional-route-parameters) specific to the route. The map supports retrieving single and multiple values from the same parameter. + +
+ queryParamMap + + + An `Observable` that contains a [map](api/router/ParamMap) of the [query parameters](#query-parameters) available to all routes. + The map supports retrieving single and multiple values from the query parameter. + +
+ fragment + + + An `Observable` of the URL [fragment](#fragment) available to all routes. + +
+ outlet + + + The name of the `RouterOutlet` used to render the route. For an unnamed outlet, the outlet name is _primary_. + +
+ routeConfig + + + The route configuration used for the route that contains the origin path. + +
+ parent + + + The route's parent `ActivatedRoute` when this route is a [child route](#child-routing-component). + +
+ firstChild + + + Contains the first `ActivatedRoute` in the list of this route's child routes. + +
+ children + + + Contains all the [child routes](#child-routing-component) activated under the current route. + +
+Two older properties are still available. They are less capable than their replacements, discouraged, and may be deprecated in a future Angular version. +**`params`** — An `Observable` that contains the required and [optional parameters](#optional-route-parameters) specific to the route. Use `paramMap` instead. -**`url`**: An `Observable` of the route path(s), represented as an array of strings for each part of the route path. - -**`data`**: An `Observable` that contains the `data` object provided for the route. Also contains any resolved values from the [resolve guard](guide/router#resolve-guard). - -**`params`**: An `Observable` that contains the required and [optional parameters](guide/router#optional-route-parameters) specific to the route. - -**`queryParams`**: An `Observable` that contains the [query parameters](guide/router#query-parameters) available to all routes. - -**`fragment`**: An `Observable` of the URL [fragment](guide/router#fragment) available to all routes. - -**`outlet`**: The name of the `RouterOutlet` used to render the route. For an unnamed outlet, the outlet name is _primary_. - -**`routeConfig`**: The route configuration used for the route that contains the origin path. - -**`parent`**: an `ActivatedRoute` that contains the information from the parent route when using [child routes](guide/router#child-routing-component). - -**`firstChild`**: contains the first `ActivatedRoute` in the list of child routes. - -**`children`**: contains all the [child routes](guide/router#child-routing-component) activated under the current route. - +**`queryParams`** — An `Observable` that contains the [query parameters](#query-parameters) available to all routes. +Use `queryParamMap` instead.
+#### _Activated Route_ in action - -Import the `Router`, `ActivatedRoute`, and `Params` tokens from the router package. +Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package. @@ -1610,53 +1805,96 @@ that the component requires and reference them as private variables. - - Later, in the `ngOnInit` method, you use the `ActivatedRoute` service to retrieve the parameters for the route, pull the hero `id` from the parameters and retrieve the hero to display. -
- - - -Put this data access logic in the `ngOnInit` method rather than inside the constructor to improve the component's testability. -Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent` -so the hero will be retrieved in time to use it. - -Learn more about the `ngOnInit` method and other component lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks) guide. - - -
- - - +The `paramMap` processing is a bit tricky. When the map changes, you `get()` +the `id` parameter from the changed parameters. +Then you tell the `HeroService` to fetch the hero with that `id` and return the result of the `HeroService` request. -Since the parameters are provided as an `Observable`, you use the `switchMap` operator to -provide them for the `id` parameter by name and tell the `HeroService` to fetch the hero with that `id`. +You might think to use the RxJS `map` operator. +But the `HeroService` returns an `Observable`. +Your subscription wants the `Hero`, not an `Observable`. +So you flatten the `Observable` with the `switchMap` operator instead. -The `switchMap` operator allows you to perform an action with the current value of the `Observable`, -and map it to a new `Observable`. As with many `rxjs` operators, `switchMap` handles -an `Observable` as well as a `Promise` to retrieve the value they emit. +The `switchMap` operator also cancels previous in-flight requests. If the user re-navigates to this route +with a new `id` while the `HeroService` is still retrieving the old `id`, `switchMap` discards that old request and returns the hero for the new `id`. -The `switchMap` operator will also cancel any in-flight requests if the user re-navigates to the route -while still retrieving a hero. +Finally, you activate the observable with `subscribe` method and (re)set the component's `hero` property with the retrieved hero. -Use the `subscribe` method to detect `id` changes and to (re)set the retrieved `Hero`. +#### _ParamMap_ API +The `ParamMap` API is inspired by the [URLSearchParams interface](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParamsOPut). It provides methods +to handle parameter access for both route parameters (`paramMap`) and query parameters (`queryParamMap`). + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Member + + Description +
+ has(name) + + + Returns `true` if the parameter name is in the map of parameters. + +
+ get(name) + + + Returns the parameter name value (a `string`) if present, or `null` if the parameter name is not in the map. Returns the _first_ element if the parameter value is actually an array of values. + +
+ getAll(name) + + + Returns a `string array` of the parameter name value if found, or an empty `array` if the parameter name value is not in the map. Use `getAll` when a single parameter could have multiple values. + +
+ keys + + + Returns a `string array` of all parameter names in the map. + +
{@a reuse} +#### Observable paramMap and component reuse -#### Observable params and component reuse - -In this example, you retrieve the route params from an `Observable`. -That implies that the route params can change during the lifetime of this component. +In this example, you retrieve the route parameter map from an `Observable`. +That implies that the route parameter map can change during the lifetime of this component. They might. By default, the router re-uses a component instance when it re-navigates to the same component type without visiting a different component first. The route parameters could change each time. @@ -1671,7 +1909,7 @@ Better to simply re-use the same component instance and update the parameter. Unfortunately, `ngOnInit` is only called once per component instantiation. You need a way to detect when the route parameters change from _within the same instance_. -The observable `params` property handles that beautifully. +The observable `paramMap` property handles that beautifully.
@@ -1706,7 +1944,7 @@ Therefore, the router creates a new `HeroDetailComponent` instance every time. When you know for certain that a `HeroDetailComponent` instance will *never, never, ever* be re-used, you can simplify the code with the *snapshot*. -The `route.snapshot` provides the initial value of the route parameters. +The `route.snapshot` provides the initial value of the route parameter map. You can access the parameters directly without subscribing or adding observable operators. It's much simpler to write and read: @@ -1721,10 +1959,10 @@ It's much simpler to write and read: -**Remember:** you only get the _initial_ value of the parameters with this technique. -Stick with the observable `params` approach if there's even a chance that the router +**Remember:** you only get the _initial_ value of the parameter map with this technique. +Stick with the observable `paramMap` approach if there's even a chance that the router could re-use the component. -This sample stays with the observable `params` strategy just in case. +This sample stays with the observable `paramMap` strategy just in case.
@@ -1753,7 +1991,7 @@ It holds the _path to the `HeroListComponent`_: ### Route Parameters: Required or optional? -Use [*route parameters*](guide/router#route-parameters) to specify a *required* parameter value *within* the route URL +Use [*route parameters*](#route-parameters) to specify a *required* parameter value *within* the route URL as you do when navigating to the `HeroDetailComponent` in order to view the hero with *id* 15: @@ -1803,7 +2041,7 @@ prefer an *optional parameter* when the value is optional, complex, and/or multi ### Heroes list: optionally selecting a hero When navigating to the `HeroDetailComponent` you specified the _required_ `id` of the hero-to-edit in the -*route parameter* and made it the second item of the [_link parameters array_](guide/router#link-parameters-array). +*route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array). @@ -1916,7 +2154,7 @@ The `HeroListComponent` isn't expecting any parameters at all and wouldn't know You can change that. Previously, when navigating from the `HeroListComponent` to the `HeroDetailComponent`, -you subscribed to the route params `Observable` and made it available to the `HeroDetailComponent` +you subscribed to the route parameter map `Observable` and made it available to the `HeroDetailComponent` in the `ActivatedRoute` service. You injected that service in the constructor of the `HeroDetailComponent`. @@ -1931,7 +2169,7 @@ First you extend the router import statement to include the `ActivatedRoute` ser -Import the `switchMap` operator to perform an operation on the `Observable` of route parameters. +Import the `switchMap` operator to perform an operation on the `Observable` of route parameter map. @@ -1949,21 +2187,8 @@ Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor. -The `ActivatedRoute.params` property is an `Observable` of route parameters. The `params` emits new `id` values -when the user navigates to the component. In `ngOnInit` you subscribe to those values, set the `selectedId`, -and get the heroes. - - -
- - - -All route/query parameters are strings. -The (+) in front of the `params['id']` expression is a JavaScript trick to convert the string to an integer. - -
- - +The `ActivatedRoute.paramMap` property is an `Observable` map of route parameters. The `paramMap` emits a new map of values that includes `id` +when the user navigates to the component. In `ngOnInit` you subscribe to those values, set the `selectedId`, and get the heroes. Add an `isSelected` method that returns `true` when a hero's `id` matches the selected `id`. @@ -2294,13 +2519,9 @@ If your app had many feature areas, the app component trees might look like this Add the following `crisis-center.component.ts` to the `crisis-center` folder: - - - + - - The `CrisisCenterComponent` has the following in common with the `AppComponent`: * It is the *root* of the crisis center area, @@ -2322,28 +2543,19 @@ instead you use the router to *navigate* to it. ### Child route configuration -The `CrisisCenterComponent` is a *routing component* like the `AppComponent`. -It has its own `RouterOutlet` and its own child routes. - -Add the following `crisis-center-home.component.ts` to the `crisis-center` folder. - - - +As a host page for the "Crisis Center" feature, add the following `crisis-center-home.component.ts` to the `crisis-center` folder. + - - Create a `crisis-center-routing.module.ts` file as you did the `heroes-routing.module.ts` file. This time, you define **child routes** *within* the parent `crisis-center` route. - - Notice that the parent `crisis-center` route has a `children` property with a single route containing the `CrisisListComponent`. The `CrisisListComponent` route also has a `children` array with two routes. @@ -2359,7 +2571,7 @@ of the `CrisisCenterComponent`, not in the `RouterOutlet` of the `AppComponent` The `CrisisListComponent` contains the crisis list and a `RouterOutlet` to display the `Crisis Center Home` and `Crisis Detail` route components. -The `Crisis Detail` route is a child of the `Crisis List`. Since the router [reuses components](guide/router#reuse) +The `Crisis Detail` route is a child of the `Crisis List`. Since the router [reuses components](#reuse) by default, the `Crisis Detail` component will be re-used as you select different crises. In contrast, back in the `Hero Detail` route, the component was recreated each time you selected a different hero. @@ -2609,7 +2821,7 @@ There are two noteworthy differences. Note that the `send()` method simulates latency by waiting a second before "sending" the message and closing the popup. The `closePopup()` method closes the popup view by navigating to the popup outlet with a `null`. -That's a peculiarity covered [below](guide/router#clear-secondary-routes). +That's a peculiarity covered [below](#clear-secondary-routes). As with other application components, you add the `ComposeMessageComponent` to the `declarations` of an `NgModule`. Do so in the `AppModule`. @@ -2736,7 +2948,7 @@ To see how, look at the `closePopup()` method again: -It navigates imperatively with the `Router.navigate()` method, passing in a [link parameters array](guide/router#link-parameters-array). +It navigates imperatively with the `Router.navigate()` method, passing in a [link parameters array](#link-parameters-array). Like the array bound to the _Contact_ `RouterLink` in the `AppComponent`, this one includes an object with an `outlets` property. @@ -2787,23 +2999,23 @@ These are all asynchronous operations. Accordingly, a routing guard can return an `Observable` or a `Promise` and the router will wait for the observable to resolve to `true` or `false`. -The router supports multiple kinds of guards: +The router supports multiple guard interfaces: -1. [`CanActivate`](api/router/CanActivate) to mediate navigation *to* a route. +* [`CanActivate`](api/router/CanActivate) to mediate navigation *to* a route. -2. [`CanActivateChild()`](api/router/CanActivateChild) to mediate navigation *to* a child route. +* [`CanActivateChild`](api/router/CanActivateChild) to mediate navigation *to* a child route. -3. [`CanDeactivate`](api/router/CanDeactivate) to mediate navigation *away* from the current route. +* [`CanDeactivate`](api/router/CanDeactivate) to mediate navigation *away* from the current route. -4. [`Resolve`](api/router/Resolve) to perform route data retrieval *before* route activation. +* [`Resolve`](api/router/Resolve) to perform route data retrieval *before* route activation. -5. [`CanLoad`](api/router/CanLoad) to mediate navigation *to* a feature module loaded _asynchronously_. +* [`CanLoad`](api/router/CanLoad) to mediate navigation *to* a feature module loaded _asynchronously_. You can have multiple guards at every level of a routing hierarchy. -The router checks the `CanDeactivate()` and `CanActivateChild()` guards first, from the deepest child route to the top. -Then it checks the `CanActivate()` guards from the top down to the deepest child route. If the feature module -is loaded asynchronously, the `CanLoad()` guard is checked before the module is loaded. +The router checks the `CanDeactivate` and `CanActivateChild` guards first, from the deepest child route to the top. +Then it checks the `CanActivate` guards from the top down to the deepest child route. If the feature module +is loaded asynchronously, the `CanLoad` guard is checked before the module is loaded. If _any_ guard returns false, pending guards that have not completed will be canceled, and the entire navigation is canceled. @@ -2936,7 +3148,7 @@ You've defined a _component-less_ route. The goal is to group the `Crisis Center` management routes under the `admin` path. You don't need a component to do it. -A _component-less_ route makes it easier to [guard child routes](guide/router#can-activate-child-guard). +A _component-less_ route makes it easier to [guard child routes](#can-activate-child-guard). Next, import the `AdminModule` into `app.module.ts` and add it to the `imports` array @@ -2968,7 +3180,7 @@ The new *admin* feature should be accessible only to authenticated users. You could hide the link until the user logs in. But that's tricky and difficult to maintain. -Instead you'll write a `CanActivate()` guard to redirect anonymous users to the +Instead you'll write a `canActivate()` guard method to redirect anonymous users to the login page when they try to enter the admin area. This is a general purpose guard—you can imagine other features @@ -2986,7 +3198,7 @@ It simply logs to console and `returns` true immediately, allowing navigation to Next, open `admin-routing.module.ts `, import the `AuthGuard` class, and -update the admin route with a `CanActivate()` guard property that references it: +update the admin route with a `canActivate` guard property that references it: @@ -3083,7 +3295,7 @@ Import and add the `LoginRoutingModule` to the `AppModule` imports as well. Guards and the service providers they require _must_ be provided at the module-level. This allows the Router access to retrieve these services from the `Injector` during the navigation process. -The same rule applies for feature modules loaded [asynchronously](guide/router#asynchronous-routing). +The same rule applies for feature modules loaded [asynchronously](#asynchronous-routing). @@ -3105,9 +3317,9 @@ You should also protect child routes _within_ the feature module. Extend the `AuthGuard` to protect when navigating between the `admin` routes. Open `auth-guard.service.ts` and add the `CanActivateChild` interface to the imported tokens from the router package. -Next, implement the `CanActivateChild` method which takes the same arguments as the `CanActivate` method: +Next, implement the `canActivateChild()` method which takes the same arguments as the `canActivate()` method: an `ActivatedRouteSnapshot` and `RouterStateSnapshot`. -The `CanActivateChild` method can return an `Observable` or `Promise` for +The `canActivateChild()` method can return an `Observable` or `Promise` for async checks and a `boolean` for sync checks. This one returns a `boolean`: @@ -3211,7 +3423,7 @@ to discard changes and navigate away (`true`) or to preserve the pending changes {@a CanDeactivate} -Create a _guard_ that checks for the presence of a `canDeactivate` method in a component—any component. +Create a _guard_ that checks for the presence of a `canDeactivate()` method in a component—any component. The `CrisisDetailComponent` will have this method. But the guard doesn't have to know that. The guard shouldn't know the details of any component's deactivation method. @@ -3249,13 +3461,13 @@ Looking back at the `CrisisDetailComponent`, it implements the confirmation work -Notice that the `canDeactivate` method *can* return synchronously; +Notice that the `canDeactivate()` method *can* return synchronously; it returns `true` immediately if there is no crisis or there are no pending changes. But it can also return a `Promise` or an `Observable` and the router will wait for that to resolve to truthy (navigate) or falsy (stay put). -Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array. +Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array property. @@ -3431,7 +3643,7 @@ The relevant *Crisis Center* code for this milestone follows. ### Query parameters and fragments -In the [route parameters](guide/router#optional-route-parameters) example, you only dealt with parameters specific to +In the [route parameters](#optional-route-parameters) example, you only dealt with parameters specific to the route, but what if you wanted optional parameters available to all routes? This is where query parameters come into play. @@ -3454,7 +3666,7 @@ Add the `NavigationExtras` object to the `router.navigate` method that navigates You can also preserve query parameters and fragments across navigations without having to provide them again when navigating. In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigate` function -and provide the `preserveQueryParams` and `preserveFragment` to pass along the current query parameters +and provide the `queryParamsHandling` and `preserveFragment` to pass along the current query parameters and fragment to the next route. @@ -3462,6 +3674,15 @@ and fragment to the next route. +
+ + +The `queryParamsHandling` feature also provides a `merge` option, which will preserve and combine the current query parameters with any provided query parameters +when navigating. + + +
+ Since you'll be navigating to the *Admin Dashboard* route after logging in, you'll update it to handle the @@ -3474,14 +3695,14 @@ query parameters and fragment. -*Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service. +*Query parameters* and *fragments* are also available through the `ActivatedRoute` service. Just like *route parameters*, the query parameters and fragments are provided as an `Observable`. The updated *Crisis Admin* component feeds the `Observable` directly into the template using the `AsyncPipe`. Now, you can click on the *Admin* button, which takes you to the *Login* -page with the provided `query params` and `fragment`. After you click the login button, notice that -you have been redirected to the `Admin Dashboard` page with the `query params` and `fragment` still intact. +page with the provided `queryParamMap` and `fragment`. After you click the login button, notice that +you have been redirected to the `Admin Dashboard` page with the query parameters and fragment still intact in the address bar. You can use these persistent bits of information for things that need to be provided across pages like authentication tokens or session ids. @@ -3492,7 +3713,7 @@ authentication tokens or session ids. The `query params` and `fragment` can also be preserved using a `RouterLink` with -the `preserveQueryParams` and `preserveFragment` bindings respectively. +the `queryParamsHandling` and `preserveFragment` bindings respectively. @@ -3596,7 +3817,7 @@ its `checkLogin()` method to support the `CanLoad` guard. Open `auth-guard.service.ts`. Import the `CanLoad` interface from `@angular/router`. Add it to the `AuthGuard` class's `implements` list. -Then implement `canLoad` as follows: +Then implement `canLoad()` as follows: @@ -3609,7 +3830,7 @@ The router sets the `canLoad()` method's `route` parameter to the intended desti The `checkLogin()` method redirects to that URL once the user has logged in. Now import the `AuthGuard` into the `AppRoutingModule` and add the `AuthGuard` to the `canLoad` -array for the `admin` route. +array property for the `admin` route. The completed admin route looks like this: @@ -3660,7 +3881,7 @@ The `Router` offers two preloading strategies out of the box: * Preloading of all lazy loaded feature areas. Out of the box, the router either never preloads, or preloads every lazy load module. -The `Router` also supports [custom preloading strategies](guide/router#custom-preloading) for +The `Router` also supports [custom preloading strategies](#custom-preloading) for fine control over which modules to preload and when. In this next section, you'll update the `CrisisCenterModule` to load lazily @@ -3732,7 +3953,7 @@ Surprisingly, the `AdminModule` does _not_ preload. Something is blocking it. #### CanLoad blocks preload -The `PreloadAllModules` strategy does not load feature areas protected by a [CanLoad](guide/router#can-load-guard) guard. +The `PreloadAllModules` strategy does not load feature areas protected by a [CanLoad](#can-load-guard) guard. This is by design. You added a `CanLoad` guard to the route in the `AdminModule` a few steps back @@ -3740,7 +3961,7 @@ to block loading of that module until the user is authorized. That `CanLoad` guard takes precedence over the preload strategy. If you want to preload a module _and_ guard against unauthorized access, -drop the `canLoad` guard and rely on the [CanActivate](guide/router#can-activate-guard) guard alone. +drop the `canLoad()` guard method and rely on the [canActivate()](#can-activate-guard) guard alone. {@a custom-preloading} @@ -3827,7 +4048,7 @@ It's also logged to the browser's console. ## Inspect the router's configuration You put a lot of effort into configuring the router in several routing module files -and were careful to list them [in the proper order](guide/router#routing-module-order). +and were careful to list them [in the proper order](#routing-module-order). Are routes actually evaluated as you planned? How is the router really configured? @@ -3841,11 +4062,9 @@ to see the finished route configuration. - {@a final-app} - ## Wrap up and final app You've covered a lot of ground in this guide and the application is too big to reprint here. @@ -4084,4 +4303,3 @@ in the `AppModule`. - diff --git a/aio/content/tutorial/toh-pt5.md b/aio/content/tutorial/toh-pt5.md index 0354398a65..bde66a4101 100644 --- a/aio/content/tutorial/toh-pt5.md +++ b/aio/content/tutorial/toh-pt5.md @@ -680,7 +680,7 @@ The major changes are driven by how you get hero names. You'll no longer receive the hero in a parent component property binding. -The new `HeroDetailComponent` should take the `id` parameter from the `params` Observable +The new `HeroDetailComponent` should take the `id` parameter from the `paramMap` Observable in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`. @@ -721,7 +721,7 @@ Tell the class to implement the `OnInit` interface. -Inside the `ngOnInit()` lifecycle hook, use the `params` Observable to +Inside the `ngOnInit()` lifecycle hook, use the `paramMap` Observable to extract the `id` parameter value from the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`. @@ -753,7 +753,7 @@ As described in the [ActivatedRoute: the one-stop-shop for route information](gu section of the [Routing & Navigation](guide/router) page, the `Router` manages the observables it provides and localizes the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against -memory leaks, so you don't need to unsubscribe from the route `params` `Observable`. +memory leaks, so you don't need to unsubscribe from the route `paramMap` `Observable`.