From 2ccdd867d2e60b07373c6c7694b427eeb276bb6d Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Mon, 2 May 2016 16:53:25 -0700 Subject: [PATCH] docs(router): samples and doc for new router (phase 1) --- public/docs/_examples/package.json | 1 + .../_examples/quickstart/js/package.1.json | 20 +- .../_examples/quickstart/ts/package.1.json | 3 +- .../_examples/router-deprecated/e2e-spec.js | 122 ++ .../ts/app/app.component.1.ts | 43 + .../ts/app/app.component.2.ts | 58 + .../ts/app/app.component.3.ts | 53 + .../router-deprecated/ts/app/app.component.ts | 44 + .../app/crisis-center/add-crisis.component.ts | 0 .../crisis-center.component.1.ts | 28 + .../crisis-center/crisis-center.component.ts | 22 + .../crisis-detail.component.1.ts | 94 ++ .../crisis-center/crisis-detail.component.ts | 84 + .../crisis-center/crisis-list.component.1.ts | 36 + .../crisis-center/crisis-list.component.ts | 40 + .../ts/app/crisis-center/crisis.service.ts | 41 + .../ts/app/crisis-list.component.ts | 10 + .../ts/app/dialog.service.ts | 18 + .../ts/app/hero-list.component.ts | 10 + .../ts/app/heroes/hero-detail.component.1.ts | 46 + .../ts/app/heroes/hero-detail.component.ts | 51 + .../ts/app/heroes/hero-list.component.1.ts | 49 + .../ts/app/heroes/hero-list.component.ts | 53 + .../ts/app/heroes/hero.service.ts | 27 + .../router-deprecated/ts/app/main.1.ts | 23 + .../router-deprecated/ts/app/main.2.ts | 32 + .../router-deprecated/ts/app/main.3.ts | 7 + .../router-deprecated/ts/app/main.ts | 7 + .../router-deprecated/ts/example-config.json | 0 .../router-deprecated/ts/index.1.html | 33 + .../router-deprecated/ts/index.2.html | 31 + .../router-deprecated/ts/index.3.html | 31 + .../_examples/router-deprecated/ts/index.html | 30 + .../router/ts/app/app.component.1.ts | 27 +- .../router/ts/app/app.component.2.ts | 58 +- .../router/ts/app/app.component.3.ts | 70 +- .../_examples/router/ts/app/app.component.ts | 47 +- .../crisis-center.component.1.ts | 18 +- .../crisis-center/crisis-center.component.ts | 18 +- .../crisis-detail.component.1.ts | 40 +- .../crisis-center/crisis-detail.component.ts | 40 +- .../crisis-center/crisis-list.component.1.ts | 19 +- .../crisis-center/crisis-list.component.ts | 34 +- .../ts/app/crisis-center/crisis.service.ts | 33 +- .../router/ts/app/crisis-list.component.ts | 2 +- .../_examples/router/ts/app/dialog.service.ts | 4 +- .../router/ts/app/hero-list.component.ts | 2 +- .../ts/app/heroes/hero-detail.component.1.ts | 27 +- .../ts/app/heroes/hero-detail.component.ts | 30 +- .../ts/app/heroes/hero-list.component.1.ts | 17 +- .../ts/app/heroes/hero-list.component.ts | 32 +- .../router/ts/app/heroes/hero.service.ts | 22 +- public/docs/_examples/router/ts/app/main.1.ts | 10 +- public/docs/_examples/router/ts/app/main.2.ts | 13 +- public/docs/_examples/router/ts/app/main.3.ts | 6 +- public/docs/_examples/router/ts/app/main.ts | 6 +- public/docs/_examples/router/ts/index.2.html | 7 +- public/docs/_examples/router/ts/index.3.html | 4 +- public/docs/_examples/router/ts/index.html | 4 +- public/docs/_examples/systemjs.config.js | 2 +- .../docs/_examples/systemjs.config.plunker.js | 2 +- public/docs/dart/latest/guide/_data.json | 8 +- .../dart/latest/guide/router-deprecated.jade | 1 + public/docs/js/latest/guide/_data.json | 8 +- .../js/latest/guide/router-deprecated.jade | 1 + public/docs/ts/latest/guide/_data.json | 9 +- public/docs/ts/latest/guide/router-aux.jade | 185 --- .../ts/latest/guide/router-deprecated.jade | 1480 +++++++++++++++++ public/docs/ts/latest/guide/router.jade | 12 +- 69 files changed, 2954 insertions(+), 491 deletions(-) create mode 100644 public/docs/_examples/router-deprecated/e2e-spec.js create mode 100644 public/docs/_examples/router-deprecated/ts/app/app.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/app.component.2.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/app.component.3.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/app.component.ts rename public/docs/_examples/{router => router-deprecated}/ts/app/crisis-center/add-crisis.component.ts (100%) create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-detail.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis.service.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/crisis-list.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/dialog.service.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/hero-list.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/heroes/hero.service.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/main.1.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/main.2.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/main.3.ts create mode 100644 public/docs/_examples/router-deprecated/ts/app/main.ts create mode 100644 public/docs/_examples/router-deprecated/ts/example-config.json create mode 100644 public/docs/_examples/router-deprecated/ts/index.1.html create mode 100644 public/docs/_examples/router-deprecated/ts/index.2.html create mode 100644 public/docs/_examples/router-deprecated/ts/index.3.html create mode 100644 public/docs/_examples/router-deprecated/ts/index.html create mode 100644 public/docs/dart/latest/guide/router-deprecated.jade create mode 100644 public/docs/js/latest/guide/router-deprecated.jade delete mode 100644 public/docs/ts/latest/guide/router-aux.jade create mode 100644 public/docs/ts/latest/guide/router-deprecated.jade diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json index f1017685de..d568c823f1 100644 --- a/public/docs/_examples/package.json +++ b/public/docs/_examples/package.json @@ -25,6 +25,7 @@ "@angular/http": "2.0.0-rc.0", "@angular/platform-browser": "2.0.0-rc.0", "@angular/platform-browser-dynamic": "2.0.0-rc.0", + "@angular/router": "2.0.0-rc.0", "@angular/router-deprecated": "2.0.0-rc.0", "@angular/upgrade": "2.0.0-rc.0", diff --git a/public/docs/_examples/quickstart/js/package.1.json b/public/docs/_examples/quickstart/js/package.1.json index 6ca4585d27..d020c77798 100644 --- a/public/docs/_examples/quickstart/js/package.1.json +++ b/public/docs/_examples/quickstart/js/package.1.json @@ -7,14 +7,22 @@ }, "license": "ISC", "dependencies": { - "@angular/common": "2.0.0-rc.0", - "@angular/compiler": "2.0.0-rc.0", - "@angular/core": "2.0.0-rc.0", - "@angular/platform-browser": "2.0.0-rc.0", - "@angular/platform-browser-dynamic": "2.0.0-rc.0", + "@angular/common": "2.0.0-rc.0", + "@angular/compiler": "2.0.0-rc.0", + "@angular/core": "2.0.0-rc.0", + "@angular/http": "2.0.0-rc.0", + "@angular/platform-browser": "2.0.0-rc.0", + "@angular/platform-browser-dynamic": "2.0.0-rc.0", + "@angular/router": "2.0.0-rc.0", + "@angular/router-deprecated": "2.0.0-rc.0", + "@angular/upgrade": "2.0.0-rc.0", + "reflect-metadata": "0.1.3", "rxjs": "5.0.0-beta.6", - "zone.js": "0.6.12" + "zone.js": "0.6.12", + + "angular2-in-memory-web-api": "0.0.6", + "bootstrap": "^3.3.6" }, "devDependencies": { "concurrently": "^2.0.0", diff --git a/public/docs/_examples/quickstart/ts/package.1.json b/public/docs/_examples/quickstart/ts/package.1.json index 16ed9c6461..d43f688043 100644 --- a/public/docs/_examples/quickstart/ts/package.1.json +++ b/public/docs/_examples/quickstart/ts/package.1.json @@ -17,6 +17,7 @@ "@angular/http": "2.0.0-rc.0", "@angular/platform-browser": "2.0.0-rc.0", "@angular/platform-browser-dynamic": "2.0.0-rc.0", + "@angular/router": "2.0.0-rc.0", "@angular/router-deprecated": "2.0.0-rc.0", "@angular/upgrade": "2.0.0-rc.0", @@ -26,7 +27,7 @@ "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", - "angular2-in-memory-web-api": "0.0.5", + "angular2-in-memory-web-api": "0.0.6", "bootstrap": "^3.3.6" }, "devDependencies": { diff --git a/public/docs/_examples/router-deprecated/e2e-spec.js b/public/docs/_examples/router-deprecated/e2e-spec.js new file mode 100644 index 0000000000..bd3f84ee83 --- /dev/null +++ b/public/docs/_examples/router-deprecated/e2e-spec.js @@ -0,0 +1,122 @@ +describe('Router', function () { + + beforeAll(function () { + browser.get(''); + }); + + function getPageStruct() { + hrefEles = element.all(by.css('my-app a')); + + return { + hrefs: hrefEles, + routerParent: element(by.css('my-app > undefined')), + routerTitle: element(by.css('my-app > undefined > h2')), + + crisisHref: hrefEles.get(0), + crisisList: element.all(by.css('my-app > undefined > undefined li')), + crisisDetail: element(by.css('my-app > undefined > undefined > div')), + crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')), + + heroesHref: hrefEles.get(1), + heroesList: element.all(by.css('my-app > undefined li')), + heroDetail: element(by.css('my-app > undefined > div')), + heroDetailTitle: element(by.css('my-app > undefined > div > h3')), + + } + } + + it('should be able to see the start screen', function () { + var page = getPageStruct(); + expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); + expect(page.crisisHref.getText()).toEqual("Crisis Center"); + expect(page.heroesHref.getText()).toEqual("Heroes"); + }); + + it('should be able to see crises center items', function () { + var page = getPageStruct(); + expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries at start"); + }); + + it('should be able to see hero items', function () { + var page = getPageStruct(); + page.heroesHref.click().then(function() { + expect(page.routerTitle.getText()).toContain('HEROES'); + expect(page.heroesList.count()).toBe(6, "should be 6 heroes"); + }); + }); + + it('should be able to toggle the views', function () { + var page = getPageStruct(); + page.crisisHref.click().then(function() { + expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries"); + return page.heroesHref.click(); + }).then(function() { + expect(page.heroesList.count()).toBe(6, "should be 6 heroes"); + }); + }); + + it('should be able to edit and save details from the crisis center view', function () { + crisisCenterEdit(2, true); + }); + + it('should be able to edit and cancel details from the crisis center view', function () { + crisisCenterEdit(3, false); + }); + + it('should be able to edit and save details from the heroes view', function () { + var page = getPageStruct(); + var heroEle, heroText; + page.heroesHref.click().then(function() { + heroEle = page.heroesList.get(4); + return heroEle.getText(); + }).then(function(text) { + expect(text.length).toBeGreaterThan(0, 'should have some text'); + // remove leading id from text + heroText = text.substr(text.indexOf(' ')).trim(); + return heroEle.click(); + }).then(function() { + expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries"); + expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); + expect(page.heroDetailTitle.getText()).toContain(heroText); + var inputEle = page.heroDetail.element(by.css('input')); + return sendKeys(inputEle, '-foo'); + }).then(function() { + expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo'); + var buttonEle = page.heroDetail.element(by.css('button')); + return buttonEle.click(); + }).then(function() { + expect(heroEle.getText()).toContain(heroText + '-foo'); + }) + }); + + function crisisCenterEdit(index, shouldSave) { + var page = getPageStruct(); + var crisisEle, crisisText; + page.crisisHref.click().then(function () { + crisisEle = page.crisisList.get(index); + return crisisEle.getText(); + }).then(function (text) { + expect(text.length).toBeGreaterThan(0, 'should have some text'); + // remove leading id from text + crisisText = text.substr(text.indexOf(' ')).trim(); + return crisisEle.click(); + }).then(function () { + expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries"); + expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); + expect(page.crisisDetailTitle.getText()).toContain(crisisText); + var inputEle = page.crisisDetail.element(by.css('input')); + return sendKeys(inputEle, '-foo'); + }).then(function () { + expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo'); + var buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel')); + return buttonEle.click(); + }).then(function () { + if (shouldSave) { + expect(crisisEle.getText()).toContain(crisisText + '-foo'); + } else { + expect(crisisEle.getText()).not.toContain(crisisText + '-foo'); + } + }); + } + +}); diff --git a/public/docs/_examples/router-deprecated/ts/app/app.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/app.component.1.ts new file mode 100644 index 0000000000..0ceeb73848 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/app.component.1.ts @@ -0,0 +1,43 @@ +/* First version */ +// #docplaster + +// #docregion +import { Component } from '@angular/core'; +// #docregion import-router +import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprecated'; +// #enddocregion import-router + +import { CrisisListComponent } from './crisis-list.component'; +import { HeroListComponent } from './hero-list.component'; + +@Component({ + selector: 'my-app', +// #docregion template + template: ` +

Component Router

+ + + `, +// #enddocregion template + directives: [ROUTER_DIRECTIVES] +}) +// #enddocregion +/* +// #docregion route-config +@Component({ ... }) +// #enddocregion route-config +*/ +// #docregion +// #docregion route-config +@RouteConfig([ +// #docregion route-defs + {path: '/crisis-center', name: 'CrisisCenter', component: CrisisListComponent}, + {path: '/heroes', name: 'Heroes', component: HeroListComponent} +// #enddocregion route-defs +]) +export class AppComponent { } +// #enddocregion route-config +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/app.component.2.ts b/public/docs/_examples/router-deprecated/ts/app/app.component.2.ts new file mode 100644 index 0000000000..ec0cb936b3 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/app.component.2.ts @@ -0,0 +1,58 @@ +/* Second Heroes version */ +// #docplaster + +// #docregion +import {Component} from '@angular/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; + +import {CrisisListComponent} from './crisis-list.component'; +// #enddocregion +/* +// Apparent Milestone 2 imports +// #docregion +// #docregion hero-import +import {HeroListComponent} from './heroes/hero-list.component'; +import {HeroDetailComponent} from './heroes/hero-detail.component'; +import {HeroService} from './heroes/hero.service'; +// #enddocregion hero-import +// #enddocregion +*/ +// Actual Milestone 2 imports +import {HeroListComponent} from './heroes/hero-list.component.1'; +import {HeroDetailComponent} from './heroes/hero-detail.component.1'; +import {HeroService} from './heroes/hero.service'; +// #docregion + +@Component({ + selector: 'my-app', + template: ` +

Component Router

+ + + `, + providers: [HeroService], + directives: [ROUTER_DIRECTIVES] +}) +// #enddocregion +/* +// #docregion route-config +@Component({ ... }) +// #enddocregion route-config +*/ +// #docregion +// #docregion route-config +@RouteConfig([ +// #docregion route-defs + {path: '/crisis-center', name: 'CrisisCenter', component: CrisisListComponent}, + {path: '/heroes', name: 'Heroes', component: HeroListComponent}, + // #docregion hero-detail-route + {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent} + // #enddocregion hero-detail-route +// #enddocregion route-defs +]) +export class AppComponent { } +// #enddocregion route-config +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/app.component.3.ts b/public/docs/_examples/router-deprecated/ts/app/app.component.3.ts new file mode 100644 index 0000000000..d0bd0b4f84 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/app.component.3.ts @@ -0,0 +1,53 @@ +// #docplaster +import {Component} from '@angular/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; + +import {CrisisCenterComponent} from './crisis-center/crisis-center.component.1'; + +import {DialogService} from './dialog.service'; +import {HeroService} from './heroes/hero.service'; + +@Component({ + selector: 'my-app', +// #enddocregion + /* Typical link + // #docregion h-anchor + Heroes + // #enddocregion h-anchor + */ + /* Incomplete Crisis Center link when CC lacks a default + // #docregion cc-anchor-fail + // The link now fails with a "non-terminal link" error + // #docregion cc-anchor-w-default + Crisis Center + // #enddocregion cc-anchor-w-default + // #enddocregion cc-anchor-fail + */ + /* Crisis Center link when CC lacks a default + // #docregion cc-anchor-no-default + Crisis Center + // #enddocregion cc-anchor-no-default + */ + /* Crisis Center Detail link + // #docregion Dragon-anchor + Dragon Crisis + // #enddocregion Dragon-anchor + */ +// #docregion template + template: ` +

Component Router

+ + + `, +// #enddocregion template + providers: [DialogService, HeroService], + directives: [ROUTER_DIRECTIVES] +}) +@RouteConfig([ + {path: '/crisis-center/...', name: 'CrisisCenter', component: CrisisCenterComponent}, +]) +export class AppComponent { } diff --git a/public/docs/_examples/router-deprecated/ts/app/app.component.ts b/public/docs/_examples/router-deprecated/ts/app/app.component.ts new file mode 100644 index 0000000000..4d8528c463 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/app.component.ts @@ -0,0 +1,44 @@ +// #docplaster +// #docregion +import {Component} from '@angular/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; + +import {CrisisCenterComponent} from './crisis-center/crisis-center.component'; +import {HeroListComponent} from './heroes/hero-list.component'; +import {HeroDetailComponent} from './heroes/hero-detail.component'; + +import {DialogService} from './dialog.service'; +import {HeroService} from './heroes/hero.service'; + +@Component({ + selector: 'my-app', +// #docregion template + template: ` +

Component Router

+ + + `, +// #enddocregion template + providers: [DialogService, HeroService], + directives: [ROUTER_DIRECTIVES] +}) +// #docregion route-config +@RouteConfig([ + + // #docregion route-config-cc + { // Crisis Center child route + path: '/crisis-center/...', + name: 'CrisisCenter', + component: CrisisCenterComponent, + useAsDefault: true + }, + // #enddocregion route-config-cc + + {path: '/heroes', name: 'Heroes', component: HeroListComponent}, + {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}, +]) +// #enddocregion route-config +export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/crisis-center/add-crisis.component.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/add-crisis.component.ts similarity index 100% rename from public/docs/_examples/router/ts/app/crisis-center/add-crisis.component.ts rename to public/docs/_examples/router-deprecated/ts/app/crisis-center/add-crisis.component.ts diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts new file mode 100644 index 0000000000..027d8df037 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts @@ -0,0 +1,28 @@ +import {Component} from '@angular/core'; +import {RouteConfig, RouterOutlet} from '@angular/router-deprecated'; + +import {CrisisListComponent} from './crisis-list.component.1'; +import {CrisisDetailComponent} from './crisis-detail.component.1'; +import {CrisisService} from './crisis.service'; + +// #docregion minus-imports +@Component({ + template: ` +

CRISIS CENTER

+ + `, + directives: [RouterOutlet], +// #docregion providers + providers: [CrisisService] +// #enddocregion providers +}) +// #docregion route-config +@RouteConfig([ + // #docregion default-route + {path:'/', name: 'CrisisList', component: CrisisListComponent, useAsDefault: true}, + // #enddocregion default-route + {path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent} +]) +// #enddocregion route-config +export class CrisisCenterComponent { } +// #enddocregion minus-imports diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.ts new file mode 100644 index 0000000000..77060d936b --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-center.component.ts @@ -0,0 +1,22 @@ +// #docregion +import {Component} from '@angular/core'; +import {RouteConfig, RouterOutlet} from '@angular/router-deprecated'; + +import {CrisisListComponent} from './crisis-list.component'; +import {CrisisDetailComponent} from './crisis-detail.component'; +import {CrisisService} from './crisis.service'; + +@Component({ + template: ` +

CRISIS CENTER

+ + `, + directives: [RouterOutlet], + providers: [CrisisService] +}) +@RouteConfig([ + {path:'/', name: 'CrisisList', component: CrisisListComponent, useAsDefault: true}, + {path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent} +]) +export class CrisisCenterComponent { } +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts new file mode 100644 index 0000000000..f92180e462 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts @@ -0,0 +1,94 @@ +// #docplaster + +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Crisis, CrisisService} from './crisis.service'; +import {RouteParams, Router} from '@angular/router-deprecated'; +// #docregion routerCanDeactivate +import {CanDeactivate, ComponentInstruction} from '@angular/router-deprecated'; +import {DialogService} from '../dialog.service'; + +// #enddocregion routerCanDeactivate + +@Component({ + // #docregion template + template: ` +
+

"{{editName}}"

+
+ {{crisis.id}}
+
+ + +
+

+ + +

+
+ `, + // #enddocregion template + styles: ['input {width: 20em}'] +}) +// #docregion routerCanDeactivate, cancel-save +export class CrisisDetailComponent implements OnInit, CanDeactivate { + + crisis: Crisis; + editName: string; + +// #enddocregion routerCanDeactivate, cancel-save + constructor( + private _service: CrisisService, + private _router: Router, + private _routeParams: RouteParams, + private _dialog: DialogService + ) { } + + // #docregion ngOnInit + ngOnInit() { + let id = +this._routeParams.get('id'); + this._service.getCrisis(id).then(crisis => { + if (crisis) { + this.editName = crisis.name; + this.crisis = crisis; + } else { // id not found + this.gotoCrises(); + } + }); + } + // #enddocregion ngOnInit + + // #docregion routerCanDeactivate + routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any { + // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. + if (!this.crisis || this.crisis.name === this.editName) { + return true; + } + // Otherwise ask the user with the dialog service and return its + // promise which resolves to true or false when the user decides + return this._dialog.confirm('Discard changes?'); + } + // #enddocregion routerCanDeactivate + + // #docregion cancel-save + cancel() { + this.editName = this.crisis.name; + this.gotoCrises(); + } + + save() { + this.crisis.name = this.editName; + this.gotoCrises(); + } + // #enddocregion cancel-save + + // #docregion gotoCrises + gotoCrises() { + // Like Crisis Center +

"{{editName}}"

+
+ {{crisis.id}}
+
+ + +
+

+ + +

+ + `, + styles: ['input {width: 20em}'] +}) + +export class CrisisDetailComponent implements OnInit, CanDeactivate { + + crisis: Crisis; + editName: string; + + constructor( + private _service: CrisisService, + private _router: Router, + private _routeParams: RouteParams, + private _dialog: DialogService + ) { } + + ngOnInit() { + let id = +this._routeParams.get('id'); + this._service.getCrisis(id).then(crisis => { + if (crisis) { + this.editName = crisis.name; + this.crisis = crisis; + } else { // id not found + this.gotoCrises(); + } + }); + } + + routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any { + // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. + if (!this.crisis || this.crisis.name === this.editName) { + return true; + } + // Otherwise ask the user with the dialog service and return its + // promise which resolves to true or false when the user decides + return this._dialog.confirm('Discard changes?'); + } + + cancel() { + this.editName = this.crisis.name; + this.gotoCrises(); + } + + save() { + this.crisis.name = this.editName; + this.gotoCrises(); + } + + // #docregion gotoCrises + gotoCrises() { + let crisisId = this.crisis ? this.crisis.id : null; + // Pass along the hero id if available + // so that the CrisisListComponent can select that hero. + // Add a totally useless `foo` parameter for kicks. + // #docregion gotoCrises-navigate + this._router.navigate(['CrisisList', {id: crisisId, foo: 'foo'} ]); + // #enddocregion gotoCrises-navigate + } + // #enddocregion gotoCrises +} diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.1.ts new file mode 100644 index 0000000000..f2fa9c770c --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.1.ts @@ -0,0 +1,36 @@ +// #docplaster + +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Crisis, CrisisService} from './crisis.service'; +import {Router} from '@angular/router-deprecated'; + +@Component({ + // #docregion template + template: ` + + `, + // #enddocregion template +}) +export class CrisisListComponent implements OnInit { + crises: Crisis[]; + + constructor( + private _service: CrisisService, + private _router: Router) {} + + ngOnInit() { + this._service.getCrises().then(crises => this.crises = crises); + } + + // #docregion select + onSelect(crisis: Crisis) { + this._router.navigate(['CrisisDetail', { id: crisis.id }] ); + } + // #enddocregion select +} diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.ts new file mode 100644 index 0000000000..3a6128302f --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis-list.component.ts @@ -0,0 +1,40 @@ +// #docplaster + +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Crisis, CrisisService} from './crisis.service'; +import {Router, RouteParams} from '@angular/router-deprecated'; + +@Component({ + template: ` + + `, +}) +export class CrisisListComponent implements OnInit { + crises: Crisis[]; + + private _selectedId: number; + + constructor( + private _service: CrisisService, + private _router: Router, + routeParams: RouteParams) { + this._selectedId = +routeParams.get('id'); + } + + isSelected(crisis: Crisis) { return crisis.id === this._selectedId; } + + ngOnInit() { + this._service.getCrises().then(crises => this.crises = crises); + } + + onSelect(crisis: Crisis) { + this._router.navigate( ['CrisisDetail', { id: crisis.id }] ); + } +} diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis.service.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis.service.ts new file mode 100644 index 0000000000..c71827a656 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-center/crisis.service.ts @@ -0,0 +1,41 @@ +// #docplaster + +// #docregion +import {Injectable} from '@angular/core'; + +export class Crisis { + constructor(public id: number, public name: string) { } +} + +@Injectable() +export class CrisisService { + getCrises() { return crisesPromise; } + + getCrisis(id: number | string) { + return crisesPromise + .then(crises => crises.filter(c => c.id === +id)[0]); + } + +// #enddocregion + + static nextCrisisId = 100; + + addCrisis(name:string) { + name = name.trim(); + if (name){ + let crisis = new Crisis(CrisisService.nextCrisisId++, name); + crisesPromise.then(crises => crises.push(crisis)); + } + } +// #docregion +} + +var crises = [ + new Crisis(1, 'Dragon Burning Cities'), + new Crisis(2, 'Sky Rains Great White Sharks'), + new Crisis(3, 'Giant Asteroid Heading For Earth'), + new Crisis(4, 'Procrastinators Meeting Delayed Again'), +]; + +var crisesPromise = Promise.resolve(crises); +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/crisis-list.component.ts b/public/docs/_examples/router-deprecated/ts/app/crisis-list.component.ts new file mode 100644 index 0000000000..9a22de266a --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/crisis-list.component.ts @@ -0,0 +1,10 @@ +// Initial empty version +// #docregion +import {Component} from '@angular/core'; + +@Component({ + template: ` +

CRISIS CENTER

+

Get your crisis here

` +}) +export class CrisisListComponent { } diff --git a/public/docs/_examples/router-deprecated/ts/app/dialog.service.ts b/public/docs/_examples/router-deprecated/ts/app/dialog.service.ts new file mode 100644 index 0000000000..c795945d2b --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/dialog.service.ts @@ -0,0 +1,18 @@ +// #docregion +import {Injectable} from '@angular/core'; +/** + * Async modal dialog service + * DialogService makes this app easier to test by faking this service. + * TODO: better modal implemenation that doesn't use window.confirm + */ +@Injectable() +export class DialogService { + /** + * Ask user to confirm an action. `message` explains the action and choices. + * Returns promise resolving to `true`=confirm or `false`=cancel + */ + confirm(message?:string) { + return new Promise((resolve, reject) => + resolve(window.confirm(message || 'Is it OK?'))); + }; +} diff --git a/public/docs/_examples/router-deprecated/ts/app/hero-list.component.ts b/public/docs/_examples/router-deprecated/ts/app/hero-list.component.ts new file mode 100644 index 0000000000..48c108dde1 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/hero-list.component.ts @@ -0,0 +1,10 @@ +/// Initial empty version +// #docregion +import {Component} from '@angular/core'; + +@Component({ + template: ` +

HEROES

+

Get your heroes here

` +}) +export class HeroListComponent { } diff --git a/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.1.ts new file mode 100644 index 0000000000..7002b9a1dd --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.1.ts @@ -0,0 +1,46 @@ +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Hero, HeroService} from './hero.service'; +import {RouteParams, Router} from '@angular/router-deprecated'; + +@Component({ + template: ` +

HEROES

+
+

"{{hero.name}}"

+
+ {{hero.id}}
+
+ + +
+

+ +

+
+ `, +}) +export class HeroDetailComponent implements OnInit { + hero: Hero; + + // #docregion ctor + constructor( + private _router:Router, + private _routeParams:RouteParams, + private _service:HeroService){} + // #enddocregion ctor + + // #docregion ngOnInit + ngOnInit() { + let id = this._routeParams.get('id'); + this._service.getHero(id).then(hero => this.hero = hero); + } + // #enddocregion ngOnInit + + // #docregion gotoHeroes + gotoHeroes() { + // Like Heroes + this._router.navigate(['Heroes']); + } + // #enddocregion gotoHeroes +} diff --git a/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.ts new file mode 100644 index 0000000000..edbf163b37 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-detail.component.ts @@ -0,0 +1,51 @@ +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Hero, HeroService} from './hero.service'; +import {RouteParams, Router} from '@angular/router-deprecated'; + +@Component({ + template: ` +

HEROES

+
+

"{{hero.name}}"

+
+ {{hero.id}}
+
+ + +
+

+ +

+
+ `, +}) +export class HeroDetailComponent implements OnInit { + hero: Hero; + + // #docregion ctor + constructor( + private _router:Router, + private _routeParams:RouteParams, + private _service:HeroService){} + // #enddocregion ctor + + // #docregion ngOnInit + ngOnInit() { + let id = this._routeParams.get('id'); + this._service.getHero(id).then(hero => this.hero = hero); + } + // #enddocregion ngOnInit + + // #docregion gotoHeroes + gotoHeroes() { + let heroId = this.hero ? this.hero.id : null; + // Pass along the hero id if available + // so that the HeroList component can select that hero. + // Add a totally useless `foo` parameter for kicks. + // #docregion gotoHeroes-navigate + this._router.navigate(['Heroes', {id: heroId, foo: 'foo'} ]); + // #enddocregion gotoHeroes-navigate + } + // #enddocregion gotoHeroes +} diff --git a/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.1.ts b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.1.ts new file mode 100644 index 0000000000..18f6c6e36a --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.1.ts @@ -0,0 +1,49 @@ +// #docplaster + +// #docregion +// TODO SOMEDAY: Feature Componetized like HeroCenter +import {Component, OnInit} from '@angular/core'; +import {Hero, HeroService} from './hero.service'; +import {Router} from '@angular/router-deprecated'; + +@Component({ + // #docregion template + template: ` +

HEROES

+ + ` + // #enddocregion template +}) +export class HeroListComponent implements OnInit { + heroes: Hero[]; + + // #docregion ctor + constructor( + private _router: Router, + private _service: HeroService) { } + // #enddocregion ctor + + ngOnInit() { + this._service.getHeroes().then(heroes => this.heroes = heroes) + } + + // #docregion select + onSelect(hero: Hero) { + // #docregion nav-to-detail + this._router.navigate( ['HeroDetail', { id: hero.id }] ); + // #enddocregion nav-to-detail + } + // #enddocregion select +} +// #enddocregion + +/* A link parameters array +// #docregion link-parameters-array +['HeroDetail', { id: hero.id }] // {id: 15} +// #enddocregion link-parameters-array +*/ \ No newline at end of file diff --git a/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.ts b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.ts new file mode 100644 index 0000000000..3a5127d11f --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/heroes/hero-list.component.ts @@ -0,0 +1,53 @@ +// #docplaster + +// TODO SOMEDAY: Feature Componetized like CrisisCenter +// #docregion +import {Component, OnInit} from '@angular/core'; +import {Hero, HeroService} from './hero.service'; +// #docregion import-route-params +import {Router, RouteParams} from '@angular/router-deprecated'; +// #enddocregion import-route-params + +@Component({ + // #docregion template + template: ` +

HEROES

+ + ` + // #enddocregion template +}) +export class HeroListComponent implements OnInit { + heroes: Hero[]; + + // #docregion ctor + private _selectedId: number; + + constructor( + private _service: HeroService, + private _router: Router, + routeParams: RouteParams) { + this._selectedId = +routeParams.get('id'); + } + // #enddocregion ctor + + // #docregion isSelected + isSelected(hero: Hero) { return hero.id === this._selectedId; } + // #enddocregion isSelected + + // #docregion select + onSelect(hero: Hero) { + this._router.navigate( ['HeroDetail', { id: hero.id }] ); + } + // #enddocregion select + + ngOnInit() { + this._service.getHeroes().then(heroes => this.heroes = heroes) + } +} +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/heroes/hero.service.ts b/public/docs/_examples/router-deprecated/ts/app/heroes/hero.service.ts new file mode 100644 index 0000000000..0b6066ee03 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/heroes/hero.service.ts @@ -0,0 +1,27 @@ +// #docregion +import {Injectable} from '@angular/core'; + +export class Hero { + constructor(public id: number, public name: string) { } +} + +@Injectable() +export class HeroService { + getHeroes() { return heroesPromise; } + + getHero(id: number | string) { + return heroesPromise + .then(heroes => heroes.filter(h => h.id === +id)[0]); + } +} + +var HEROES = [ + new Hero(11, 'Mr. Nice'), + new Hero(12, 'Narco'), + new Hero(13, 'Bombasto'), + new Hero(14, 'Celeritas'), + new Hero(15, 'Magneta'), + new Hero(16, 'RubberMan') +]; + +var heroesPromise = Promise.resolve(HEROES); diff --git a/public/docs/_examples/router-deprecated/ts/app/main.1.ts b/public/docs/_examples/router-deprecated/ts/app/main.1.ts new file mode 100644 index 0000000000..d9a288e6c0 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/main.1.ts @@ -0,0 +1,23 @@ +/* First version */ +// #docplaster + +// #docregion all +import {AppComponent} from './app.component'; +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; + +// #enddocregion all + +/* Can't use AppComponent ... but display as if we can +// #docregion all +bootstrap(AppComponent, [ +// #enddocregion all +*/ + +// Actually use the v.1 component +import {AppComponent as ac} from './app.component.1'; +bootstrap(ac, [ +// #docregion all + ROUTER_PROVIDERS +]); +// #enddocregion all \ No newline at end of file diff --git a/public/docs/_examples/router-deprecated/ts/app/main.2.ts b/public/docs/_examples/router-deprecated/ts/app/main.2.ts new file mode 100644 index 0000000000..d984713151 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/main.2.ts @@ -0,0 +1,32 @@ +/* Second version */ +// For Milestone #2 +// Also includes digression on HashPathStrategy (not used in the final app) +// #docplaster + +// #docregion +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; +import {AppComponent} from './app.component'; + +// Add these symbols to override the `LocationStrategy` +import {provide} from '@angular/core'; +import {LocationStrategy, + HashLocationStrategy} from '@angular/common'; +// #enddocregion +/* Can't use AppComponent ... but display as if we can +// #docregion + +bootstrap(AppComponent, [ +// #enddocregion +*/ + +// Actually use the v.2 component +import {AppComponent as ac} from './app.component.2'; + +bootstrap(ac, [ +// #docregion + ROUTER_PROVIDERS, + provide(LocationStrategy, + {useClass: HashLocationStrategy}) // .../#/crisis-center/ +]); +// #enddocregion diff --git a/public/docs/_examples/router-deprecated/ts/app/main.3.ts b/public/docs/_examples/router-deprecated/ts/app/main.3.ts new file mode 100644 index 0000000000..2c84a01d02 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/main.3.ts @@ -0,0 +1,7 @@ +// #docregion +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; + +import {AppComponent} from './app.component.3'; + +bootstrap(AppComponent, [ROUTER_PROVIDERS]); diff --git a/public/docs/_examples/router-deprecated/ts/app/main.ts b/public/docs/_examples/router-deprecated/ts/app/main.ts new file mode 100644 index 0000000000..eb0b424738 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/app/main.ts @@ -0,0 +1,7 @@ +// #docregion +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; + +import {AppComponent} from './app.component'; + +bootstrap(AppComponent, [ROUTER_PROVIDERS]); diff --git a/public/docs/_examples/router-deprecated/ts/example-config.json b/public/docs/_examples/router-deprecated/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/router-deprecated/ts/index.1.html b/public/docs/_examples/router-deprecated/ts/index.1.html new file mode 100644 index 0000000000..6bc706e2e6 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/index.1.html @@ -0,0 +1,33 @@ + + + + + + + + Router Sample v.1 + + + + + + + + + + + + + + + + +

Milestone 1

+ loading... + + + + diff --git a/public/docs/_examples/router-deprecated/ts/index.2.html b/public/docs/_examples/router-deprecated/ts/index.2.html new file mode 100644 index 0000000000..c25f8fb366 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/index.2.html @@ -0,0 +1,31 @@ + + + + + + Router Sample v.2 + + + + + + + + + + + + + + + + +

Milestone 2

+ loading... + + + + diff --git a/public/docs/_examples/router-deprecated/ts/index.3.html b/public/docs/_examples/router-deprecated/ts/index.3.html new file mode 100644 index 0000000000..1be32f1d0d --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/index.3.html @@ -0,0 +1,31 @@ + + + + + + Router Sample v.3 + + + + + + + + + + + + + + + + +

Milestone 3

+ loading... + + + + diff --git a/public/docs/_examples/router-deprecated/ts/index.html b/public/docs/_examples/router-deprecated/ts/index.html new file mode 100644 index 0000000000..58b2d0c7f7 --- /dev/null +++ b/public/docs/_examples/router-deprecated/ts/index.html @@ -0,0 +1,30 @@ + + + + + + + Router Sample + + + + + + + + + + + + + + + + + loading... + + + + diff --git a/public/docs/_examples/router/ts/app/app.component.1.ts b/public/docs/_examples/router/ts/app/app.component.1.ts index 0ceeb73848..274bb61909 100644 --- a/public/docs/_examples/router/ts/app/app.component.1.ts +++ b/public/docs/_examples/router/ts/app/app.component.1.ts @@ -3,12 +3,10 @@ // #docregion import { Component } from '@angular/core'; -// #docregion import-router -import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprecated'; -// #enddocregion import-router +import { Routes, ROUTER_DIRECTIVES } from '@angular/router'; -import { CrisisListComponent } from './crisis-list.component'; -import { HeroListComponent } from './hero-list.component'; +import { CrisisListComponent } from './crisis-list.component'; +import { HeroListComponent } from './hero-list.component'; @Component({ selector: 'my-app', @@ -16,8 +14,8 @@ import { HeroListComponent } from './hero-list.component'; template: `

Component Router

`, @@ -26,16 +24,17 @@ import { HeroListComponent } from './hero-list.component'; }) // #enddocregion /* -// #docregion route-config -@Component({ ... }) -// #enddocregion route-config -*/ + // #docregion route-config + @Component({ ... }) + // #enddocregion route-config + */ // #docregion // #docregion route-config -@RouteConfig([ +@Routes([ // #docregion route-defs - {path: '/crisis-center', name: 'CrisisCenter', component: CrisisListComponent}, - {path: '/heroes', name: 'Heroes', component: HeroListComponent} + {path: '/crisis-center', component: CrisisListComponent}, + {path: '/heroes', component: HeroListComponent}, + {path: '*', component: CrisisListComponent} // #enddocregion route-defs ]) export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/app.component.2.ts b/public/docs/_examples/router/ts/app/app.component.2.ts index ec0cb936b3..f50d0435a8 100644 --- a/public/docs/_examples/router/ts/app/app.component.2.ts +++ b/public/docs/_examples/router/ts/app/app.component.2.ts @@ -2,25 +2,25 @@ // #docplaster // #docregion -import {Component} from '@angular/core'; -import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; +import { Component, OnInit } from '@angular/core'; +import { Routes, ROUTER_DIRECTIVES, Router } from '@angular/router'; -import {CrisisListComponent} from './crisis-list.component'; +import { CrisisListComponent } from './crisis-list.component'; // #enddocregion /* -// Apparent Milestone 2 imports -// #docregion -// #docregion hero-import -import {HeroListComponent} from './heroes/hero-list.component'; -import {HeroDetailComponent} from './heroes/hero-detail.component'; -import {HeroService} from './heroes/hero.service'; -// #enddocregion hero-import -// #enddocregion -*/ + // Apparent Milestone 2 imports + // #docregion + // #docregion hero-import + import { HeroListComponent } from './heroes/hero-list.component'; + import { HeroDetailComponent } from './heroes/hero-detail.component'; + import { HeroService } from './heroes/hero.service'; + // #enddocregion hero-import + // #enddocregion + */ // Actual Milestone 2 imports -import {HeroListComponent} from './heroes/hero-list.component.1'; -import {HeroDetailComponent} from './heroes/hero-detail.component.1'; -import {HeroService} from './heroes/hero.service'; +import { HeroListComponent } from './heroes/hero-list.component.1'; +import { HeroDetailComponent } from './heroes/hero-detail.component.1'; +import { HeroService } from './heroes/hero.service'; // #docregion @Component({ @@ -28,8 +28,8 @@ import {HeroService} from './heroes/hero.service'; template: `

Component Router

`, @@ -38,21 +38,27 @@ import {HeroService} from './heroes/hero.service'; }) // #enddocregion /* -// #docregion route-config -@Component({ ... }) -// #enddocregion route-config -*/ + // #docregion route-config + @Component({ ... }) + // #enddocregion route-config + */ // #docregion // #docregion route-config -@RouteConfig([ +@Routes([ // #docregion route-defs - {path: '/crisis-center', name: 'CrisisCenter', component: CrisisListComponent}, - {path: '/heroes', name: 'Heroes', component: HeroListComponent}, + {path: '/crisis-center', component: CrisisListComponent}, + {path: '/heroes', component: HeroListComponent}, // #docregion hero-detail-route - {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent} + {path: '/hero/:id', component: HeroDetailComponent} // #enddocregion hero-detail-route // #enddocregion route-defs ]) -export class AppComponent { } +export class AppComponent implements OnInit { + constructor(private router: Router) {} + + ngOnInit() { + this.router.navigate(['/crisis-center']); + } +} // #enddocregion route-config // #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.component.3.ts b/public/docs/_examples/router/ts/app/app.component.3.ts index d0bd0b4f84..928b374475 100644 --- a/public/docs/_examples/router/ts/app/app.component.3.ts +++ b/public/docs/_examples/router/ts/app/app.component.3.ts @@ -1,45 +1,48 @@ +/* tslint:disable:no-unused-variable */ // #docplaster -import {Component} from '@angular/core'; -import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; +import { Component, OnInit } from '@angular/core'; +import { Routes, ROUTER_DIRECTIVES, Router } from '@angular/router'; -import {CrisisCenterComponent} from './crisis-center/crisis-center.component.1'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component.1'; +import { HeroListComponent } from './heroes/hero-list.component.1'; +import { HeroDetailComponent } from './heroes/hero-detail.component.1'; -import {DialogService} from './dialog.service'; -import {HeroService} from './heroes/hero.service'; +import { DialogService } from './dialog.service'; +import { HeroService } from './heroes/hero.service'; @Component({ selector: 'my-app', // #enddocregion /* Typical link - // #docregion h-anchor - Heroes - // #enddocregion h-anchor - */ + // #docregion h-anchor + Heroes + // #enddocregion h-anchor + */ /* Incomplete Crisis Center link when CC lacks a default - // #docregion cc-anchor-fail - // The link now fails with a "non-terminal link" error - // #docregion cc-anchor-w-default - Crisis Center - // #enddocregion cc-anchor-w-default - // #enddocregion cc-anchor-fail - */ + // #docregion cc-anchor-fail + // The link now fails with a "non-terminal link" error + // #docregion cc-anchor-w-default + Crisis Center + // #enddocregion cc-anchor-w-default + // #enddocregion cc-anchor-fail + */ /* Crisis Center link when CC lacks a default - // #docregion cc-anchor-no-default - Crisis Center - // #enddocregion cc-anchor-no-default - */ + // #docregion cc-anchor-no-default + Crisis Center + // #enddocregion cc-anchor-no-default + */ /* Crisis Center Detail link - // #docregion Dragon-anchor - Dragon Crisis - // #enddocregion Dragon-anchor - */ + // #docregion Dragon-anchor + Dragon Crisis + // #enddocregion Dragon-anchor + */ // #docregion template template: `

Component Router

`, @@ -47,7 +50,14 @@ import {HeroService} from './heroes/hero.service'; providers: [DialogService, HeroService], directives: [ROUTER_DIRECTIVES] }) -@RouteConfig([ - {path: '/crisis-center/...', name: 'CrisisCenter', component: CrisisCenterComponent}, +@Routes([ + {path: '/crisis-center', component: CrisisCenterComponent}, + {path: '*', component: CrisisCenterComponent} ]) -export class AppComponent { } +export class AppComponent implements OnInit { + constructor(private router: Router) {} + + ngOnInit() { + this.router.navigate(['/crisis-center']); + } +} diff --git a/public/docs/_examples/router/ts/app/app.component.ts b/public/docs/_examples/router/ts/app/app.component.ts index 4d8528c463..aeee375e61 100644 --- a/public/docs/_examples/router/ts/app/app.component.ts +++ b/public/docs/_examples/router/ts/app/app.component.ts @@ -1,14 +1,14 @@ // #docplaster // #docregion -import {Component} from '@angular/core'; -import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; +import { Component, OnInit } from '@angular/core'; +import { Routes, Router, ROUTER_DIRECTIVES } from '@angular/router'; -import {CrisisCenterComponent} from './crisis-center/crisis-center.component'; -import {HeroListComponent} from './heroes/hero-list.component'; -import {HeroDetailComponent} from './heroes/hero-detail.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { HeroListComponent } from './heroes/hero-list.component'; +import { HeroDetailComponent } from './heroes/hero-detail.component'; -import {DialogService} from './dialog.service'; -import {HeroService} from './heroes/hero.service'; +import { DialogService } from './dialog.service'; +import { HeroService } from './heroes/hero.service'; @Component({ selector: 'my-app', @@ -16,8 +16,8 @@ import {HeroService} from './heroes/hero.service'; template: `

Component Router

`, @@ -25,20 +25,17 @@ import {HeroService} from './heroes/hero.service'; providers: [DialogService, HeroService], directives: [ROUTER_DIRECTIVES] }) -// #docregion route-config -@RouteConfig([ - - // #docregion route-config-cc - { // Crisis Center child route - path: '/crisis-center/...', - name: 'CrisisCenter', - component: CrisisCenterComponent, - useAsDefault: true - }, - // #enddocregion route-config-cc - - {path: '/heroes', name: 'Heroes', component: HeroListComponent}, - {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}, +// #docregion routes +@Routes([ + {path: '/crisis-center', component: CrisisCenterComponent}, + {path: '/heroes', component: HeroListComponent}, + {path: '/hero/:id', component: HeroDetailComponent}, ]) -// #enddocregion route-config -export class AppComponent { } +// #enddocregion routes +export class AppComponent implements OnInit { + constructor(private router: Router) {} + + ngOnInit() { + this.router.navigate(['/crisis-center']); + } +} diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts index 027d8df037..05f429401a 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts @@ -1,9 +1,9 @@ -import {Component} from '@angular/core'; -import {RouteConfig, RouterOutlet} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Routes, ROUTER_DIRECTIVES } from '@angular/router'; -import {CrisisListComponent} from './crisis-list.component.1'; -import {CrisisDetailComponent} from './crisis-detail.component.1'; -import {CrisisService} from './crisis.service'; +import { CrisisListComponent } from './crisis-list.component.1'; +import { CrisisDetailComponent } from './crisis-detail.component.1'; +import { CrisisService } from './crisis.service'; // #docregion minus-imports @Component({ @@ -11,17 +11,17 @@ import {CrisisService} from './crisis.service';

CRISIS CENTER

`, - directives: [RouterOutlet], + directives: [ROUTER_DIRECTIVES], // #docregion providers providers: [CrisisService] // #enddocregion providers }) // #docregion route-config -@RouteConfig([ +@Routes([ // #docregion default-route - {path:'/', name: 'CrisisList', component: CrisisListComponent, useAsDefault: true}, + {path: '/', component: CrisisListComponent}, // , useAsDefault: true}, // coming soon // #enddocregion default-route - {path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent} + {path: '/:id', component: CrisisDetailComponent} ]) // #enddocregion route-config export class CrisisCenterComponent { } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts index 77060d936b..427ef1ed79 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts @@ -1,22 +1,22 @@ // #docregion -import {Component} from '@angular/core'; -import {RouteConfig, RouterOutlet} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Routes, ROUTER_DIRECTIVES } from '@angular/router'; -import {CrisisListComponent} from './crisis-list.component'; -import {CrisisDetailComponent} from './crisis-detail.component'; -import {CrisisService} from './crisis.service'; +import { CrisisListComponent } from './crisis-list.component'; +import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisService } from './crisis.service'; @Component({ template: `

CRISIS CENTER

`, - directives: [RouterOutlet], + directives: [ROUTER_DIRECTIVES], providers: [CrisisService] }) -@RouteConfig([ - {path:'/', name: 'CrisisList', component: CrisisListComponent, useAsDefault: true}, - {path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent} +@Routes([ + {path: '', component: CrisisListComponent}, // , useAsDefault: true}, // coming soon + {path: '/:id', component: CrisisDetailComponent} ]) export class CrisisCenterComponent { } // #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts index f92180e462..ca77bed356 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts @@ -1,15 +1,13 @@ // #docplaster - // #docregion -import {Component, OnInit} from '@angular/core'; -import {Crisis, CrisisService} from './crisis.service'; -import {RouteParams, Router} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Crisis, CrisisService } from './crisis.service'; +import { Router, OnActivate, RouteSegment } from '@angular/router'; // #docregion routerCanDeactivate -import {CanDeactivate, ComponentInstruction} from '@angular/router-deprecated'; -import {DialogService} from '../dialog.service'; +// import { CanDeactivate } from '@angular/router'; +import { DialogService } from '../dialog.service'; // #enddocregion routerCanDeactivate - @Component({ // #docregion template template: ` @@ -31,23 +29,22 @@ import {DialogService} from '../dialog.service'; styles: ['input {width: 20em}'] }) // #docregion routerCanDeactivate, cancel-save -export class CrisisDetailComponent implements OnInit, CanDeactivate { +export class CrisisDetailComponent implements OnActivate {// , CanDeactivate { crisis: Crisis; editName: string; // #enddocregion routerCanDeactivate, cancel-save constructor( - private _service: CrisisService, - private _router: Router, - private _routeParams: RouteParams, - private _dialog: DialogService + private service: CrisisService, + private router: Router, + private dialog: DialogService ) { } - // #docregion ngOnInit - ngOnInit() { - let id = +this._routeParams.get('id'); - this._service.getCrisis(id).then(crisis => { + // #docregion ngOnActivate + routerOnActivate(curr: RouteSegment): void { + let id = +curr.getParam('id'); + this.service.getCrisis(id).then(crisis => { if (crisis) { this.editName = crisis.name; this.crisis = crisis; @@ -56,17 +53,18 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate { } }); } - // #enddocregion ngOnInit + // #enddocregion ngOnActivate // #docregion routerCanDeactivate - routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any { + // NOT IMPLEMENTED YET + routerCanDeactivate(): any { // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. if (!this.crisis || this.crisis.name === this.editName) { return true; } // Otherwise ask the user with the dialog service and return its // promise which resolves to true or false when the user decides - return this._dialog.confirm('Discard changes?'); + return this.dialog.confirm('Discard changes?'); } // #enddocregion routerCanDeactivate @@ -84,8 +82,8 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate { // #docregion gotoCrises gotoCrises() { - // Like Crisis CenterCrisis Center { + routerOnActivate(curr: RouteSegment) { + this.curSegment = curr; + + let id = +curr.getParam('id'); + this.service.getCrisis(id).then(crisis => { if (crisis) { this.editName = crisis.name; this.crisis = crisis; @@ -50,18 +49,17 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate { }); } - routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any { + routerCanDeactivate(): any { // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged. if (!this.crisis || this.crisis.name === this.editName) { return true; } // Otherwise ask the user with the dialog service and return its // promise which resolves to true or false when the user decides - return this._dialog.confirm('Discard changes?'); + return this.dialog.confirm('Discard changes?'); } cancel() { - this.editName = this.crisis.name; this.gotoCrises(); } @@ -77,7 +75,11 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate { // so that the CrisisListComponent can select that hero. // Add a totally useless `foo` parameter for kicks. // #docregion gotoCrises-navigate - this._router.navigate(['CrisisList', {id: crisisId, foo: 'foo'} ]); + // Absolute link + this.router.navigate(['/crisis-center', {id: crisisId, foo: 'foo'}]); + + // Relative link + // this.router.navigate(['../', {id: crisisId, foo: 'foo'}], this.curSegment); // #enddocregion gotoCrises-navigate } // #enddocregion gotoCrises diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts index f2fa9c770c..429cb432a7 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts @@ -1,9 +1,8 @@ // #docplaster - // #docregion -import {Component, OnInit} from '@angular/core'; -import {Crisis, CrisisService} from './crisis.service'; -import {Router} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Crisis, CrisisService } from './crisis.service'; +import { Router, OnActivate, RouteSegment } from '@angular/router'; @Component({ // #docregion template @@ -17,20 +16,20 @@ import {Router} from '@angular/router-deprecated'; `, // #enddocregion template }) -export class CrisisListComponent implements OnInit { +export class CrisisListComponent implements OnActivate { crises: Crisis[]; constructor( - private _service: CrisisService, - private _router: Router) {} + private service: CrisisService, + private router: Router) {} - ngOnInit() { - this._service.getCrises().then(crises => this.crises = crises); + routerOnActivate(curr: RouteSegment): void { + this.service.getCrises().then(crises => this.crises = crises); } // #docregion select onSelect(crisis: Crisis) { - this._router.navigate(['CrisisDetail', { id: crisis.id }] ); + this.router.navigateByUrl( `/crisis-list/${crisis.id}`); } // #enddocregion select } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts index 3a6128302f..5a34dcf263 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts @@ -1,9 +1,8 @@ // #docplaster - // #docregion -import {Component, OnInit} from '@angular/core'; -import {Crisis, CrisisService} from './crisis.service'; -import {Router, RouteParams} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Crisis, CrisisService } from './crisis.service'; +import { Router, OnActivate, RouteSegment, RouteTree } from '@angular/router'; @Component({ template: ` @@ -16,25 +15,28 @@ import {Router, RouteParams} from '@angular/router-deprecated'; `, }) -export class CrisisListComponent implements OnInit { +export class CrisisListComponent implements OnActivate { crises: Crisis[]; - - private _selectedId: number; + private currSegment: RouteSegment; + private selectedId: number; constructor( - private _service: CrisisService, - private _router: Router, - routeParams: RouteParams) { - this._selectedId = +routeParams.get('id'); - } + private service: CrisisService, + private router: Router) { } - isSelected(crisis: Crisis) { return crisis.id === this._selectedId; } + isSelected(crisis: Crisis) { return crisis.id === this.selectedId; } - ngOnInit() { - this._service.getCrises().then(crises => this.crises = crises); + routerOnActivate(curr: RouteSegment, prev: RouteSegment, currTree: RouteTree) { + this.currSegment = curr; + this.selectedId = +currTree.parent(curr).getParam('id'); + this.service.getCrises().then(crises => this.crises = crises); } onSelect(crisis: Crisis) { - this._router.navigate( ['CrisisDetail', { id: crisis.id }] ); + // Absolute link + // this.router.navigate([`/crisis-center`, crisis.id]); + + // Relative link + this.router.navigate([`./${crisis.id}`], this.currSegment); } } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts index c71827a656..46158da480 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts @@ -1,14 +1,26 @@ // #docplaster -// #docregion -import {Injectable} from '@angular/core'; - export class Crisis { constructor(public id: number, public name: string) { } } +const CRISES = [ + new Crisis(1, 'Dragon Burning Cities'), + new Crisis(2, 'Sky Rains Great White Sharks'), + new Crisis(3, 'Giant Asteroid Heading For Earth'), + new Crisis(4, 'Procrastinators Meeting Delayed Again'), +]; + +let crisesPromise = Promise.resolve(CRISES); + +// #docregion +import { Injectable } from '@angular/core'; + @Injectable() export class CrisisService { + + static nextCrisisId = 100; + getCrises() { return crisesPromise; } getCrisis(id: number | string) { @@ -18,24 +30,13 @@ export class CrisisService { // #enddocregion - static nextCrisisId = 100; - - addCrisis(name:string) { + addCrisis(name: string) { name = name.trim(); - if (name){ + if (name) { let crisis = new Crisis(CrisisService.nextCrisisId++, name); crisesPromise.then(crises => crises.push(crisis)); } } // #docregion } - -var crises = [ - new Crisis(1, 'Dragon Burning Cities'), - new Crisis(2, 'Sky Rains Great White Sharks'), - new Crisis(3, 'Giant Asteroid Heading For Earth'), - new Crisis(4, 'Procrastinators Meeting Delayed Again'), -]; - -var crisesPromise = Promise.resolve(crises); // #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-list.component.ts b/public/docs/_examples/router/ts/app/crisis-list.component.ts index 9a22de266a..6caa3653b5 100644 --- a/public/docs/_examples/router/ts/app/crisis-list.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-list.component.ts @@ -1,6 +1,6 @@ // Initial empty version // #docregion -import {Component} from '@angular/core'; +import { Component } from '@angular/core'; @Component({ template: ` diff --git a/public/docs/_examples/router/ts/app/dialog.service.ts b/public/docs/_examples/router/ts/app/dialog.service.ts index c795945d2b..b6e234b6ea 100644 --- a/public/docs/_examples/router/ts/app/dialog.service.ts +++ b/public/docs/_examples/router/ts/app/dialog.service.ts @@ -1,5 +1,5 @@ // #docregion -import {Injectable} from '@angular/core'; +import { Injectable } from '@angular/core'; /** * Async modal dialog service * DialogService makes this app easier to test by faking this service. @@ -11,7 +11,7 @@ export class DialogService { * Ask user to confirm an action. `message` explains the action and choices. * Returns promise resolving to `true`=confirm or `false`=cancel */ - confirm(message?:string) { + confirm(message?: string) { return new Promise((resolve, reject) => resolve(window.confirm(message || 'Is it OK?'))); }; diff --git a/public/docs/_examples/router/ts/app/hero-list.component.ts b/public/docs/_examples/router/ts/app/hero-list.component.ts index 48c108dde1..5dbbe17d8e 100644 --- a/public/docs/_examples/router/ts/app/hero-list.component.ts +++ b/public/docs/_examples/router/ts/app/hero-list.component.ts @@ -1,6 +1,6 @@ /// Initial empty version // #docregion -import {Component} from '@angular/core'; +import { Component } from '@angular/core'; @Component({ template: ` diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts index 7002b9a1dd..bb503b748d 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts @@ -1,7 +1,7 @@ // #docregion -import {Component, OnInit} from '@angular/core'; -import {Hero, HeroService} from './hero.service'; -import {RouteParams, Router} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Hero, HeroService } from './hero.service'; +import { Router, OnActivate, RouteSegment } from '@angular/router'; @Component({ template: ` @@ -20,27 +20,26 @@ import {RouteParams, Router} from '@angular/router-deprecated'; `, }) -export class HeroDetailComponent implements OnInit { +export class HeroDetailComponent implements OnActivate { hero: Hero; // #docregion ctor constructor( - private _router:Router, - private _routeParams:RouteParams, - private _service:HeroService){} + private router: Router, + private service: HeroService) {} // #enddocregion ctor - // #docregion ngOnInit - ngOnInit() { - let id = this._routeParams.get('id'); - this._service.getHero(id).then(hero => this.hero = hero); + // #docregion OnActivate + routerOnActivate(curr: RouteSegment): void { + let id = +curr.getParam('id'); + this.service.getHero(id).then(hero => this.hero = hero); } - // #enddocregion ngOnInit + // #enddocregion OnActivate // #docregion gotoHeroes gotoHeroes() { - // Like Heroes - this._router.navigate(['Heroes']); + // Like Heroes + this.router.navigate(['/heroes']); } // #enddocregion gotoHeroes } diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts index edbf163b37..e1befcc981 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts @@ -1,7 +1,7 @@ // #docregion -import {Component, OnInit} from '@angular/core'; -import {Hero, HeroService} from './hero.service'; -import {RouteParams, Router} from '@angular/router-deprecated'; +import { Component } from '@angular/core'; +import { Hero, HeroService } from './hero.service'; +import { Router, OnActivate, RouteSegment } from '@angular/router'; @Component({ template: ` @@ -20,32 +20,30 @@ import {RouteParams, Router} from '@angular/router-deprecated'; `, }) -export class HeroDetailComponent implements OnInit { +export class HeroDetailComponent implements OnActivate { hero: Hero; // #docregion ctor constructor( - private _router:Router, - private _routeParams:RouteParams, - private _service:HeroService){} + private router: Router, + private service: HeroService) {} // #enddocregion ctor - // #docregion ngOnInit - ngOnInit() { - let id = this._routeParams.get('id'); - this._service.getHero(id).then(hero => this.hero = hero); - } - // #enddocregion ngOnInit - // #docregion gotoHeroes + // #docregion OnActivate + routerOnActivate(curr: RouteSegment): void { + let id = +curr.getParam('id'); + this.service.getHero(id).then(hero => this.hero = hero); + } + // #enddocregion OnActivate + gotoHeroes() { let heroId = this.hero ? this.hero.id : null; // Pass along the hero id if available // so that the HeroList component can select that hero. // Add a totally useless `foo` parameter for kicks. // #docregion gotoHeroes-navigate - this._router.navigate(['Heroes', {id: heroId, foo: 'foo'} ]); + this.router.navigate([`/heroes`, {id: heroId, foo: 'foo'}]); // #enddocregion gotoHeroes-navigate } - // #enddocregion gotoHeroes } diff --git a/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts b/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts index 18f6c6e36a..47015ba5e1 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-list.component.1.ts @@ -1,5 +1,4 @@ // #docplaster - // #docregion // TODO SOMEDAY: Feature Componetized like HeroCenter import {Component, OnInit} from '@angular/core'; @@ -24,18 +23,18 @@ export class HeroListComponent implements OnInit { // #docregion ctor constructor( - private _router: Router, - private _service: HeroService) { } + private router: Router, + private service: HeroService) { } // #enddocregion ctor ngOnInit() { - this._service.getHeroes().then(heroes => this.heroes = heroes) + this.service.getHeroes().then(heroes => this.heroes = heroes) } // #docregion select onSelect(hero: Hero) { // #docregion nav-to-detail - this._router.navigate( ['HeroDetail', { id: hero.id }] ); + this.router.navigate(['/hero', hero.id]); // #enddocregion nav-to-detail } // #enddocregion select @@ -43,7 +42,7 @@ export class HeroListComponent implements OnInit { // #enddocregion /* A link parameters array -// #docregion link-parameters-array -['HeroDetail', { id: hero.id }] // {id: 15} -// #enddocregion link-parameters-array -*/ \ No newline at end of file + // #docregion link-parameters-array + ['HeroDetail', { id: hero.id }] // {id: 15} + // #enddocregion link-parameters-array + */ diff --git a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts index 3a5127d11f..ef9466dbeb 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts @@ -1,11 +1,10 @@ // #docplaster - -// TODO SOMEDAY: Feature Componetized like CrisisCenter // #docregion -import {Component, OnInit} from '@angular/core'; -import {Hero, HeroService} from './hero.service'; +// TODO SOMEDAY: Feature Componetized like CrisisCenter +import { Component } from '@angular/core'; +import { Hero, HeroService} from './hero.service'; // #docregion import-route-params -import {Router, RouteParams} from '@angular/router-deprecated'; +import { Router, RouteSegment, Tree, OnActivate, RouteTree } from '@angular/router'; // #enddocregion import-route-params @Component({ @@ -22,32 +21,31 @@ import {Router, RouteParams} from '@angular/router-deprecated'; ` // #enddocregion template }) -export class HeroListComponent implements OnInit { +export class HeroListComponent implements OnActivate { heroes: Hero[]; // #docregion ctor - private _selectedId: number; + private selectedId: number; constructor( - private _service: HeroService, - private _router: Router, - routeParams: RouteParams) { - this._selectedId = +routeParams.get('id'); - } + private service: HeroService, + private router: Router) { } // #enddocregion ctor + routerOnActivate(curr: RouteSegment, prev?: RouteSegment, currTree?: RouteTree, prevTree?: RouteTree): void { + this.selectedId = +curr.getParam('id'); + this.service.getHeroes().then(heroes => this.heroes = heroes); + } + // #docregion isSelected - isSelected(hero: Hero) { return hero.id === this._selectedId; } + isSelected(hero: Hero) { return hero.id === this.selectedId; } // #enddocregion isSelected // #docregion select onSelect(hero: Hero) { - this._router.navigate( ['HeroDetail', { id: hero.id }] ); + this.router.navigate(['/hero', hero.id]); } // #enddocregion select - ngOnInit() { - this._service.getHeroes().then(heroes => this.heroes = heroes) - } } // #enddocregion diff --git a/public/docs/_examples/router/ts/app/heroes/hero.service.ts b/public/docs/_examples/router/ts/app/heroes/hero.service.ts index 0b6066ee03..7ead04d9a4 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero.service.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero.service.ts @@ -5,6 +5,17 @@ export class Hero { constructor(public id: number, public name: string) { } } +let HEROES = [ + new Hero(11, 'Mr. Nice'), + new Hero(12, 'Narco'), + new Hero(13, 'Bombasto'), + new Hero(14, 'Celeritas'), + new Hero(15, 'Magneta'), + new Hero(16, 'RubberMan') +]; + +let heroesPromise = Promise.resolve(HEROES); + @Injectable() export class HeroService { getHeroes() { return heroesPromise; } @@ -14,14 +25,3 @@ export class HeroService { .then(heroes => heroes.filter(h => h.id === +id)[0]); } } - -var HEROES = [ - new Hero(11, 'Mr. Nice'), - new Hero(12, 'Narco'), - new Hero(13, 'Bombasto'), - new Hero(14, 'Celeritas'), - new Hero(15, 'Magneta'), - new Hero(16, 'RubberMan') -]; - -var heroesPromise = Promise.resolve(HEROES); diff --git a/public/docs/_examples/router/ts/app/main.1.ts b/public/docs/_examples/router/ts/app/main.1.ts index d9a288e6c0..92ff416326 100644 --- a/public/docs/_examples/router/ts/app/main.1.ts +++ b/public/docs/_examples/router/ts/app/main.1.ts @@ -2,10 +2,10 @@ // #docplaster // #docregion all -import {AppComponent} from './app.component'; -import {bootstrap} from '@angular/platform-browser-dynamic'; -import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { ROUTER_PROVIDERS } from '@angular/router'; +import { AppComponent } from './app.component'; // #enddocregion all /* Can't use AppComponent ... but display as if we can @@ -15,9 +15,9 @@ bootstrap(AppComponent, [ */ // Actually use the v.1 component -import {AppComponent as ac} from './app.component.1'; +import { AppComponent as ac } from './app.component.1'; bootstrap(ac, [ // #docregion all ROUTER_PROVIDERS ]); -// #enddocregion all \ No newline at end of file +// #enddocregion all diff --git a/public/docs/_examples/router/ts/app/main.2.ts b/public/docs/_examples/router/ts/app/main.2.ts index d984713151..a26f927fef 100644 --- a/public/docs/_examples/router/ts/app/main.2.ts +++ b/public/docs/_examples/router/ts/app/main.2.ts @@ -4,14 +4,15 @@ // #docplaster // #docregion -import {bootstrap} from '@angular/platform-browser-dynamic'; -import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; -import {AppComponent} from './app.component'; +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { ROUTER_PROVIDERS } from '@angular/router'; + +import { AppComponent } from './app.component'; // Add these symbols to override the `LocationStrategy` -import {provide} from '@angular/core'; -import {LocationStrategy, - HashLocationStrategy} from '@angular/common'; +import { provide } from '@angular/core'; +import { LocationStrategy, + HashLocationStrategy } from '@angular/common'; // #enddocregion /* Can't use AppComponent ... but display as if we can // #docregion diff --git a/public/docs/_examples/router/ts/app/main.3.ts b/public/docs/_examples/router/ts/app/main.3.ts index 2c84a01d02..9ee2055ee6 100644 --- a/public/docs/_examples/router/ts/app/main.3.ts +++ b/public/docs/_examples/router/ts/app/main.3.ts @@ -1,7 +1,7 @@ // #docregion -import {bootstrap} from '@angular/platform-browser-dynamic'; -import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { ROUTER_PROVIDERS } from '@angular/router'; -import {AppComponent} from './app.component.3'; +import { AppComponent } from './app.component.3'; bootstrap(AppComponent, [ROUTER_PROVIDERS]); diff --git a/public/docs/_examples/router/ts/app/main.ts b/public/docs/_examples/router/ts/app/main.ts index eb0b424738..34079f84f0 100644 --- a/public/docs/_examples/router/ts/app/main.ts +++ b/public/docs/_examples/router/ts/app/main.ts @@ -1,7 +1,7 @@ // #docregion -import {bootstrap} from '@angular/platform-browser-dynamic'; -import {ROUTER_PROVIDERS} from '@angular/router-deprecated'; +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { ROUTER_PROVIDERS } from '@angular/router'; -import {AppComponent} from './app.component'; +import { AppComponent } from './app.component'; bootstrap(AppComponent, [ROUTER_PROVIDERS]); diff --git a/public/docs/_examples/router/ts/index.2.html b/public/docs/_examples/router/ts/index.2.html index ea4c410a09..d5676ff4b5 100644 --- a/public/docs/_examples/router/ts/index.2.html +++ b/public/docs/_examples/router/ts/index.2.html @@ -2,9 +2,7 @@ - - Router Sample v.2 @@ -29,5 +27,10 @@ loading... + +

Milestone 2

+ loading... + + diff --git a/public/docs/_examples/router/ts/index.3.html b/public/docs/_examples/router/ts/index.3.html index 718343fd62..1be32f1d0d 100644 --- a/public/docs/_examples/router/ts/index.3.html +++ b/public/docs/_examples/router/ts/index.3.html @@ -2,10 +2,8 @@ - - - Router Sample v.4 + Router Sample v.3 diff --git a/public/docs/_examples/router/ts/index.html b/public/docs/_examples/router/ts/index.html index d65a547332..63e60b7c11 100644 --- a/public/docs/_examples/router/ts/index.html +++ b/public/docs/_examples/router/ts/index.html @@ -1,12 +1,10 @@ + - - - Router Sample diff --git a/public/docs/_examples/systemjs.config.js b/public/docs/_examples/systemjs.config.js index bd66896d18..e5171eb56d 100644 --- a/public/docs/_examples/systemjs.config.js +++ b/public/docs/_examples/systemjs.config.js @@ -27,8 +27,8 @@ '@angular/http', '@angular/platform-browser', '@angular/platform-browser-dynamic', + '@angular/router', '@angular/router-deprecated', - '@angular/testing', '@angular/upgrade', ]; diff --git a/public/docs/_examples/systemjs.config.plunker.js b/public/docs/_examples/systemjs.config.plunker.js index 157e142e18..e6fc42f22a 100644 --- a/public/docs/_examples/systemjs.config.plunker.js +++ b/public/docs/_examples/systemjs.config.plunker.js @@ -29,8 +29,8 @@ '@angular/http', '@angular/platform-browser', '@angular/platform-browser-dynamic', + '@angular/router', '@angular/router-deprecated', - '@angular/testing', '@angular/upgrade', ]; diff --git a/public/docs/dart/latest/guide/_data.json b/public/docs/dart/latest/guide/_data.json index 68921b910a..9749d3d2f7 100644 --- a/public/docs/dart/latest/guide/_data.json +++ b/public/docs/dart/latest/guide/_data.json @@ -100,9 +100,15 @@ "intro": "Pipes transform displayed values within a template." }, + "router-deprecated": { + "title": "Beta Router (Deprecated)", + "intro": "The deprecated Beta Router." + }, + "router": { "title": "Routing & Navigation", - "intro": "Discover the basics of screen navigation with the Angular 2 router." + "intro": "Discover the basics of screen navigation with the Angular 2 router.", + "hide": true }, "structural-directives": { diff --git a/public/docs/dart/latest/guide/router-deprecated.jade b/public/docs/dart/latest/guide/router-deprecated.jade new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/dart/latest/guide/router-deprecated.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/js/latest/guide/_data.json b/public/docs/js/latest/guide/_data.json index f4f44835c0..5f168cc658 100644 --- a/public/docs/js/latest/guide/_data.json +++ b/public/docs/js/latest/guide/_data.json @@ -99,9 +99,15 @@ "intro": "Pipes transform displayed values within a template." }, + "router-deprecated": { + "title": "Beta Router (Deprecated)", + "intro": "The deprecated Beta Router." + }, + "router": { "title": "Routing & Navigation", - "intro": "Discover the basics of screen navigation with the Angular 2 router." + "intro": "Discover the basics of screen navigation with the Angular 2 router.", + "hide": true }, "structural-directives": { diff --git a/public/docs/js/latest/guide/router-deprecated.jade b/public/docs/js/latest/guide/router-deprecated.jade new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/js/latest/guide/router-deprecated.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/ts/latest/guide/_data.json b/public/docs/ts/latest/guide/_data.json index d5a21617e9..9a377e3c6f 100644 --- a/public/docs/ts/latest/guide/_data.json +++ b/public/docs/ts/latest/guide/_data.json @@ -99,9 +99,16 @@ "intro": "Pipes transform displayed values within a template." }, + + "router-deprecated": { + "title": "Beta Router (Deprecated)", + "intro": "The deprecated Beta Router." + }, + "router": { "title": "Routing & Navigation", - "intro": "Discover the basics of screen navigation with the Angular 2 router." + "intro": "Discover the basics of screen navigation with the Angular 2 Component Router.", + "hide": true }, "structural-directives": { diff --git a/public/docs/ts/latest/guide/router-aux.jade b/public/docs/ts/latest/guide/router-aux.jade deleted file mode 100644 index e44662b202..0000000000 --- a/public/docs/ts/latest/guide/router-aux.jade +++ /dev/null @@ -1,185 +0,0 @@ -// - TODO: REVIVE AUX ROUTE MATERIAL WHEN THAT FEATURE WORKS AS EXPECTED - - PLEASE DO NOT CREATE ISSUES OR PULL REQUESTS FOR THIS PAGE - - - .l-main-section - :marked - ## Milestone #4: Auxiliary Routes - Auxiliary routes are routes that can be activated independently of the current - route. They are entirely optional, depending on your app needs. - - For example, your application may have a modal that appears and this could - be an auxiliary route. The modal may have its own navigation needs, such as a slideshow - and that auxiliary route is able to manage the navigation stack independently of the - primary routes. - - In our sample application, we also want to have a chat feature that allows people - the ability to have a live agent assist them. The chat window will have first an - initial route that contains a prompt to ask the visitor if they'd like to chat with - an agent. Once they initiate a chat, they go to a new route for the chat experience. - - .alert.is-critical Make diagram of chat routes - - :marked - In this auxiliary chat experience, it overlays the current screen and persists. - If you navigate from the Heroes to Crisis Center, the chat auxiliary route remains - active and in view. - - Therefore the auxiliary routing is truly independent of the other - routing. In most respects, an auxiliary route behaves the same outside of it is rendered - in its own outlet and modifies the url differently. - - We'll look at how to setup an auxiliary route and considerations for when to use them. - - ### Auxiliary Route Outlet - In order to get an auxiliary route, it needs a place to be rendered. So far the app has - a single `RouterOutet` that the rest of our routes are rendered into. Auxiliary routes need to - have their own `RouterOutlet`, and that is done by giving it a name attribute. Open the - `app.component.ts` file and let's add the new outlet to the template. - .alert.is-critical Should remove app.component.4.ts based example (next) when we know what's what - +_makeExample('router/ts/app/app.component.4.ts', 'chat-outlet', 'app/app.component.ts') - .alert.is-critical Should be able to project from app.component.ts like this - +_makeExample('router/ts/app/app.component.ts', 'template', 'app/app.component.ts (excerpt)') - :marked - The name of the outlet must be unique to the component. You could reuse the name across - different components, so you don't have to worry about collisions. - - Here we give it a name of "chat", which will be used by the router when we setup our - route configs. The app component needs to know about this Auxiliary route, so we - import the `ChatComponent`, add a new ROUTE_NAME (`chat`), - and add a new 'Chat' route to the `ROUTES` in `app.routes.ts` (just below the redirect) . - +_makeExample('router/ts/app/routes.ts', null, 'app/routes.ts') - :marked - Look again at the 'Chat' route - +_makeExample('router/ts/app/routes.ts','chat-route') - :marked - You can see the route definition is nearly the same, except instead of `path` there is - an `aux`. The `aux` property makes this an Auxiliary route. - - @TODO Explain how a named outlet is paired with an aux route. - - The chat component defines its own routes just like the other components, even though - it is an Auxiliary route. - - +_makeExample('router/ts/app/chat/routes.ts', null, 'app/chat/routes.ts') - :marked - Even though this is an Auxiliary route, you notice there is no difference in how we've - configured the route config for the primary chat component. The chat component also has - the `RouterOutlet` Directive in the template so the child components render inside of - the chat window. - - In the chat components, we can use `RouterLink` to reference routes just the same as - a normal route. Since this is inside of an Auxiliary route, these relative links will - resolve within the chat component and not change the primary route (the Crisis Center or - Heroes pages). - - +_makeExample('router/ts/app/chat/chat-init.component.ts', 'chat-links') - - :marked - When the chat component opens, it first initializes this template to ask the user if - they'd like to chat or not. If they agree, it takes them to the chat window where they - begin to send messages to the 'live' agent. - - The behavior of the chat components may be interesting, but have no additional insights - for routing, except for the ability to deactivate an active Auxiliary route. - - ### Exiting an Auxiliary Route - - @TODO Figure out how to close/deactivate an aux route - - ### Auxiliary Route URLs - - Auxiliary Routes do modify the url using parens, like so. - code-example(format=".", language="bash"). - localhost:3002/crisis-center(chat)/2(details) - :marked - This would be the url on a page where the user was viewing an item in the Crisis Center, - in this case the "Dragon Burning Cities" crisis, and the `(chat)` Auxiliary Route would - active and on the details child route. - - ### Multiple Auxiliary Routes - - There is no limit to how many Auxiliary Routes you have defined or active. There is probably - a practical limit where too much appears on the screen for a user, but you can have as many - Auxiliary Routes as you have named `RouteOutlet`s. - - :marked - ### Auxiliary Route Summary - - * Auxiliary routes are normal routes that are rendered outside of the primary `RouterOutlet` - * They must use a named `RouterOutlet` to render. - * Can be activated as long as the parent component is active. - * Links inside of child components are resolved against the aux parent component. - * Auxiliary routes are deactivated by @TODO? - * Routes are indicated in the url using parens. - * Multiple aux routes can be active at once. - - ### Chat - The "Chat" feature area within the `chat` folder looks like this: - ``` - app/ - chat/ - ├── chat-detail.component.ts - ├── chat-init.component.ts - ├── chat.component.ts - ├── chat.service.ts - └── routes.ts - ``` - +_makeTabs( - `router/ts/app/chat/chat.component.ts, - router/ts/app/chat/routes.ts, - router/ts/app/chat/chat-init.component.ts, - router/ts/app/chat/chat-detail.component.ts, - router/ts/app/chat/chat.service.ts - `, - null, - `chat.component.ts, - chat/routes.ts, - chat-init.component.ts, - chat-detail.component.ts, - chat.service.ts, - `) - - - The following are styles extracted from `styles.css` - that only belong if/when we add chat back - ``` - /* chat styles */ - .chat { - position: fixed; - bottom: 0; - right: 20px; - border: 1px solid #1171a3; - width: 400px; - height: 300px; - } - .chat h2 { - background: #1171a3; - color: #fff; - margin: 0; - padding: 8px; - } - .chat .close { - float: right; - display: block; - padding: 0 10px; - cursor: pointer; - } - .chat .chat-content { - padding: 10px; - } - .chat .chat-messages { - height: 190px; - overflow-y: scroll; - } - .chat .chat-input { - border-top: 1px solid #ccc; - padding-top: 10px; - } - .chat .chat-input input { - width: 370px; - padding: 3px; - } - ``` diff --git a/public/docs/ts/latest/guide/router-deprecated.jade b/public/docs/ts/latest/guide/router-deprecated.jade new file mode 100644 index 0000000000..c8785640cf --- /dev/null +++ b/public/docs/ts/latest/guide/router-deprecated.jade @@ -0,0 +1,1480 @@ +include ../_util-fns + +.alert.is-critical + :marked + This chapter describes the *deprecated beta* Component Router which is + replaced by the *release candidate* Component Router. We are documenting that now. + +:marked + The Angular ***Component Router*** enables navigation from one [view](./glossary.html#view) to the next + as users perform application tasks. + + We cover the router's primary features in this chapter, illustrating them through the evolution + of a small application that we can [run live](/resources/live-examples/router/ts/plnkr.html). +.l-sub-section + img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px") + :marked + To see the URL changes in the browser address bar, + pop out the preview window by clicking the blue 'X' button in the upper right corner. + +.l-main-section +:marked + ## Overview + + The browser is a familiar model of application navigation. + We enter a URL in the address bar and the browser navigates to a corresponding page. + We click links on the page and the browser navigates to a new page. + We click the browser's back and forward buttons and the browser navigates + backward and forward through the history of pages we've seen. + + The Angular ***Component Router*** ("the router") borrows from this model. + It can interpret a browser URL as an instruction + to navigate to a client-generated view and pass optional parameters along to the supporting view component + to help it decide what specific content to present. + We can bind the router to links on a page and it will navigate to + the appropriate application view when the user clicks a link. + We can navigate imperatively when the user clicks a button, selects from a drop box, + or in response to some other stimulus from any source. And the router logs activity + in the browser's history journal so the back and forward buttons work as well. + + We'll learn many router details in this chapter which covers + + * Setting the [base href](#base-href) + * Importing from the [router library](#import) + * [configuring a router](#route-config) + * the [link parameters array](#link-parameters-array) that propels router navigation + * navigating when the user clicks a data-bound [RouterLink](#router-link) + * navigating under [program control](#navigate) + * embedding critical information in the URL with [route parameters](#route-parameters) + * creating a [child router](#child-router) with its own routes + * setting a [default route](#default) + * confirming or canceling navigation with [router lifecycle hooks](#lifecycle-hooks) + * passing optional information in [query parameters](#query-parameters) + * 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 + up to a modular, multi-view design with child routes. + + But first, an overview of router basics. + +.l-main-section +:marked + ## The Basics + Let's begin with a few core concepts of the Component Router. + Then we can explore the details through a sequence of examples. + +:marked + ### *<base href>* + Most routing applications should add a `` element to the **`index.html`** just after the `` tag + to tell the router how to compose navigation URLs. + + If the `app` folder is the application root, as it is for our sample application, + set the `href` value *exactly* as shown here. ++makeExample('router-deprecated/ts/index.1.html','base-href', 'index.html (base href)')(format=".") + +:marked + ### Router imports + The Angular Component Router is an optional service that presents a particular component view for a given URL. + It is not part of the Angular 2 core. It is in its own library package, `'@angular/router-deprecated`. + We import what we need from it as we would from any other Angular package. + ++makeExample('router-deprecated/ts/app/app.component.1.ts','import-router', 'app/app.component.ts (import)')(format=".") +.l-sub-section + :marked + We cover other options in the [details below](#browser-url-styles). +:marked + ### Configuration + When the browser's URL changes, the router looks for a corresponding **`RouteDefinition`** + from which it can determine the component to display. + + A router has no route definitions until we configure it. + The preferred way to simultaneously create a router and add its routes is with a **`@RouteConfig`** [decorator](glossary.html#decorator) + applied to the router's host component. + + In this example, we configure the top-level `AppComponent` with three route definitions ++makeExample('router-deprecated/ts/app/app.component.2.ts', 'route-config', 'app.component.ts (excerpt)')(format=".") +:marked + +.l-sub-section + :marked + There are several flavors of `RouteDefinition`. + The most common by far is the named **`Route`** which maps a URL path to a component + + The `name` field is the route name which **must** be spelled in **PascalCase** + to avoid potential confusion with the route `path`. + + The `:id` in the third route is a token for a route parameter. In a URL such as `/hero/42`, "42" + is the value of the `id` parameter. The corresponding `HeroDetailComponent` + will use that value to find and present the hero whose `id` is 42. + We'll learn more about route parameters later in this chapter. +:marked + ### Router Outlet + Now we know how the router gets its configuration. + When the browser URL for this application becomes `/heroes`, + the router matches that URL to the `RouteDefinition` named *Heroes* and displays the `HeroListComponent` + in a **`RouterOutlet`** that we've placed in the host view's HTML. +code-example(format="", language="html"). + <!-- Routed views go here --> + <router-outlet></router-outlet> +:marked + ### Router Links + Now we have routes configured and a place to render them, but + how do we navigate? The URL could arrive directly from the browser address bar. + But most of the time we navigate as a result of some user action such as the click of + an anchor tag. + + We add a **`RouterLink`** directive to the anchor tag and bind it to a template expression that + returns an array of route link parameters (the **link parameters array**). The router ultimately resolves that array + into a URL and a component view. + + We see such bindings in the following `AppComponent` template: ++makeExample('router-deprecated/ts/app/app.component.1.ts', 'template')(format=".") +.l-sub-section + :marked + We're adding two anchor tags with `RouterLink` directives. + We bind each `RouterLink` to an array containing the string name of a route definition. + 'CrisisCenter' and 'Heroes' are the names of the `Routes` we configured above. + + We'll learn to write more complex link expressions — and why they are arrays — + [later](#link-parameters-array) in the chapter. +:marked + ### Let's summarize + + The `@RouterConfig` configuration tied the `AppComponent` to a router configured with routes. + The component has a `RouterOutlet` where it can display views produced by the router. + It has `RouterLinks` that users can click to navigate via the router. + + The `AppComponent` has become a ***Routing Component***, a component that can route. + + Here are the key *Component Router* terms and their meanings: +table + tr + th Router Part + th Meaning + tr + td Router + td. + Displays the application component for the active URL. + Manages navigation from one component to the next. + tr + td @RouteConfig + td. + Configures a router with RouteDefinitions, each mapping a URL path to a component. + tr + td RouteDefinition + td. + Defines how the router should navigate to a component based on a URL pattern. + tr + td Route + td. + The most common form of RouteDefinition consisting of a path, a route name, + and a component type. + tr + td RouterOutlet + td. + The directive (<router-outlet>) that marks where the router should display a view. + tr + td RouterLink + td. + The directive for binding a clickable HTML element to + a route. Clicking an anchor tag with a routerLink directive + that is bound to a Link Parameters Array triggers a navigation. + tr + td Link Parameters Array + td. + An array that the router inteprets into a routing instruction. + We can bind a RouterLink to that array or pass the array as an argument to + the Router.navigate method. + tr + td Routing Component + td. + An Angular component with an attached router. +:marked + We've barely touched the surface of the router and its capabilities. + + The following detail sections describe a sample routing application + as it evolves over a sequence of milestones. + We strongly recommend taking the time to read and understand this story. + +.l-main-section +:marked + ## The Sample Application + We have an application in mind as we move from milestone to milestone. + +.l-sub-section + :marked + While we make incremental progress toward the ultimate sample application, this chapter is not a tutorial. + We discuss code and design decisions pertinent to routing and application design. + We gloss over everything in between. + + The full source is available in the [live example](/resources/live-examples/router/ts/plnkr.html). +:marked + Our client is the Hero Employment Agency. + Heroes need work and The Agency finds Crises for them to solve. + + The application has two main feature areas: + 1. A *Crisis Center* where we maintain the list of crises for assignment to heroes. + 1. A *Heroes* area where we maintain the list of heroes employed by The Agency. + + Run the [live example](/resources/live-examples/router/ts/plnkr.html). + It opens in the *Crisis Center*. We'll come back to that. + + Click the *Heroes* link. We're presented with a list of Heroes. +figure.image-display + img(src='/resources/images/devguide/router/hero-list.png' alt="Hero List" width="250") +:marked + We select one and the application takes us to a hero editing screen. +figure.image-display + img(src='/resources/images/devguide/router/hero-detail.png' alt="Crisis Center Detail" width="250") +:marked + Our changes take effect immediately. We click the "Back" button and the + app returns us to the Heroes list. + + We could have clicked the browser's back button instead. + That would have returned us to the Heroes List as well. + Angular app navigation updates the browser history as normal web navigation does. + + Now click the *Crisis Center* link. We go to the *Crisis Center* and its list of ongoing crises. +figure.image-display + img(src='/resources/images/devguide/router/crisis-center-list.png' alt="Crisis Center List" ) +:marked + We select one and the application takes us to a crisis editing screen. +figure.image-display + img(src='/resources/images/devguide/router/crisis-center-detail.png' alt="Crisis Center Detail") +:marked + This is a bit different from the *Hero Detail*. *Hero Detail* saves the changes as we type. + In *Crisis Detail* our changes are temporary until we either save or discard them by pressing the "Save" or "Cancel" buttons. + Both buttons navigate back to the *Crisis Center* and its list of crises. + + Suppose we click a crisis, make a change, but ***do not click either button***. + Maybe we click the browser back button instead. Maybe we click the "Heroes" link. + + Do either. Up pops a dialog box. +figure.image-display + img(src='/resources/images/devguide/router/confirm-dialog.png' alt="Confirm Dialog" width="300") +:marked + We can say "OK" and lose our changes or click "Cancel" and continue editing. + + The router supports a `routerCanDeactivate` lifecycle hook that gives us a chance to clean-up + or ask the user's permission before navigating away from the current view. + + Here we see an entire user session that touches all of these features. + +figure.image-display + img(src='/resources/images/devguide/router/router-anim.gif' alt="App in action" ) +:marked + Here's a diagram of all application routing options: +figure.image-display + img(src='/resources/images/devguide/router/complete-nav.png' alt="Navigation diagram" ) +:marked + This app illustrates the router features we'll cover in this chapter + + * navigating to a component (*Heroes* link to "Heroes List") + * including a route parameter (passing the Hero `id` while routing to the "Hero Detail") + * child routes (the *Crisis Center* has its own routes) + * the `routerCanDeactivate` lifecycle hook (ask permission to discard unsaved changes) + + +.l-main-section +:marked + ## Milestone #1: Getting Started with the Router + + Let's begin with a simple version of the app that navigates between two empty views. +figure.image-display + img(src='/resources/images/devguide/router/router-1-anim.gif' alt="App in action" ) + + +:marked + + ### Set the *<base href>* + The Component Router uses the browser's + [history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries) + for navigation. Thanks to `pushState`, we can make our in-app URL paths look the way we want them to + look, e.g. `localhost:3000/crisis-center`. Our in-app URLs can be indistinguishable from server URLs. + + Modern HTML 5 browsers were the first to support `pushState` which is why many people refer to these URLs as + "HTML 5 style" URLs. + + We must **add a [<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag** + to the `index.html` to make `pushState` routing work. + The browser also needs the base `href` value to prefix *relative* URLs when downloading and linking to + css files, scripts, and images. + + Add the base element just after the `` tag. + If the `app` folder is the application root, as it is for our application, + set the `href` value in **`index.html`** *exactly* as shown here. + ++makeExample('router-deprecated/ts/index.1.html','base-href', 'index.html (base href)')(format=".") +.l-sub-section + :marked + HTML 5 style navigation is the Component Router default. + Learn why "HTML 5" style is preferred, how to adjust its behavior, and how to switch to the + older hash (#) style if necessary in the [Browser URL Styles](#browser-url-styles) appendix below. + +:marked +.l-sub-section + :marked + #### Live example note + We have to get tricky when we run the live example because the host service sets + the application base address dynamically. That's why we replace the `` with a + script that writes a `` tag on the fly to match. + code-example(format="") + <script>document.write('<base href="' + document.location + '" />');</script> + :marked + We should only need this trick for the live example, not production code. + + +:marked + ### Import from the Component Router library + The Component Router is not part of the Angular 2 core. It is in its own library. + The router is an optional service because not all applications need routing and, + depending on your requirements, you may need a different routing library. + + The Component Router library is in its own `'@angular/router-deprecated` package. + We import what we need from it as we would from any Angular package: ++makeExample('router-deprecated/ts/app/app.component.1.ts','import-router', 'app/app.component.ts (import)')(format=".") + +:marked + ### Booting with the router service providers + Our app launches from the `main.ts` file in the `/app` folder so let's start there. + It's short and all of it is relevant to routing. ++makeExample('router-deprecated/ts/app/main.1.ts','all', 'main.ts')(format=".") +:marked + We import our root `AppComponent` and Angular's `bootstrap` function as expected. + + We also import `ROUTER_PROVIDERS` from the router library. + The router is a service implemented by a collection of *Dependency Injection* providers, most of which are identified in the + `ROUTER_PROVIDERS` array. + + We're booting Angular with `AppComponent` as our app's root component and + registering providers, as we often do, in the providers array in the second parameter of the `bootstrap` function. + Providing the router providers at the root makes the Component Router available everywhere in our application. +.l-sub-section + :marked + Learn about providers, the `provide` function, and injected services in the + [Dependency Injection chapter](dependency-injection.html). +:marked + ### The *AppComponent* shell + The root `AppComponent` is the application shell. It has title at the top, a navigation bar with two links, + and a *Router Outlet* at the bottom where the router swaps views on and off the page. Here's what we mean: +figure.image-display + img(src='/resources/images/devguide/router/shell-and-outlet.png' alt="Shell" width="300" ) +:marked + + The corresponding component template looks like this: ++makeExample('router-deprecated/ts/app/app.component.1.ts','template')(format=".") +:marked + ### *RouterOutlet* + `RouterOutlet` is a component from the router library. + The router displays views within the bounds of the `` tags. + +.l-sub-section + :marked + A template may hold exactly one ***unnamed*** ``. + + +:marked + ### *RouterLink* binding + Above the outlet, within the anchor tags, we see [Property Bindings](template-syntax.html#property-binding) to + the `RouterLink` directive that look like `[routerLink]="[...]"`. We imported `RouterLink` from the router library. + + The template expression to the right of the equals (=) returns a *link parameters array*. + + A link parameters array holds the ingredients for router navigation: + * the name of the route that prescribes the destination component and a path for the URL + * the optional route and query parameters that go into the route URL + + The arrays in this example each have a single string parameter, the name of a `Route` that + we'll configure for this application with `@RouteConfig()`. We don't need to set route parameters yet. +.l-sub-section + :marked + Learn more about the link parameters array in the [appendix below](#link-parameters-array). + +:marked + ### *@RouteConfig()* + A router holds a list of route definitions. The list is empty for a new router. We must configure it. + + A router also needs a **Host Component**, a point of origin for its navigations. + + It's natural to combine the creation of a new router, its configuration, and its assignment to a host component + in a single step. That's the purpose of the `@RouteConfig` decorator which we put to good use here: ++makeExample('router-deprecated/ts/app/app.component.1.ts','route-config')(format=".") +:marked + The `@RouteConfig` decorator creates a new router. + We applied the decorator to `AppComponent` which makes that the router's host component. + The argument to `@RouteConfig()` is an array of **Route Definitions**. + + We're supplying two definitions: ++makeExample('router-deprecated/ts/app/app.component.1.ts','route-defs')(format=".") +:marked + Each definition translates to a [Route](../api/router/Route-class.html) which has a + * `path` - the URL path segment for this route + * `name` - the name of the route + * `component` - the component associated with this route. + + The router draws upon its registry of route definition when + 1. the browser URL changes + 2. we tell the router to go to a named route + + In plain English, we might say of the first route: + 1. *When the browser's location URL changes to **match the path** segment `/crisis-center`, create or retrieve an instance of + the `CrisisCenterComponent` and display its view.* + + 1. *When the application requests navigation to a route **named** `CrisisCenter`, compose a browser URL + with the path segment `/crisis-center`, update the browser's address location and history, create or retrieve an instance of + the `CrisisListComponent`, and display that component's list view.* + + ### "Getting Started" wrap-up + + We've got a very basic, navigating app, one that can switch between two views + when the user clicks a link. + + We've learned how to + * load the router library + * add a nav bar to the shell template with anchor tags and `routerLink` directives + * added a `router-outlet` to the shell template where views will be displayed + * configure the router with `@RouterConfig` + * set the router to compose "HTML 5" browser URLs. + + The rest of the starter app is mundane, with little interest from a router perspective. + Here are the details for readers inclined to build the sample through to this milestone. + + Our starter app's structure looks like this: +.filetree + .file router-sample + .children + .file app + .children + .file app.component.ts + .file crisis-list.component.ts + .file hero-list.component.ts + .file main.ts + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file tsconfig.json + .file typings.json +:marked + Here are the files discussed in this milestone ++makeTabs( + `router/ts/app/app.component.1.ts, + router/ts/app/main.1.ts, + router/ts/app/hero-list.component.ts, + router/ts/app/crisis-list.component.ts, + router/ts/index.html`, + ',all,,', + `app.component.ts, + main.ts, + hero-list.component.ts, + crisis-list.component.ts, + index.html`) +:marked + + +.l-main-section +:marked + ## Milestone #2: The Heroes Feature + + We've seen how to navigate using the `RouterLink` directive. + + Now we'll learn some new tricks such as how to + * organize our app into *feature areas* + * navigate imperatively from one component to another + * pass information along in route parameters (`RouteParams`) + + To demonstrate, we'll build out the *Heroes* feature. + + ### The Heroes "feature area" + + A typical application has multiple *feature areas*, each an island of functionality + with its own workflow(s), dedicated to a particular business purpose. + + We could continue to add files to the `app/` folder. + That's unrealistic and ultimately not maintainable. + We think it's better to put each feature area in its own folder. + + Our first step is to **create a separate `app/heroes/` folder** + and add *Hero Management* feature files there. + + We won't be creative about it. Our example is pretty much a + copy of the code and capabilities in the "[Tutorial: Tour of Heroes](../tutorial/index.html)". + + Here's how the user will experience this version of the app +figure.image-display + img(src='/resources/images/devguide/router/router-2-anim.gif' alt="App in action" ) +:marked + ### Add Heroes functionality + + We delete the placeholder `hero-list.component.ts` that's in + the `app/` folder. + + We create a new `hero-list.component.ts` in the `app/heroes/` + folder and copy over the contents of the final `heroes.component.ts` from the tutorial. + We also copy the `hero-detail.component.ts` and the `hero.service.ts` files + into the `heroes/` folder. + + When we're done organizing, we have three *Hero Management* files: + +.filetree + .file app/heroes + .children + .file hero-detail.component.ts + .file hero-list.component.ts + .file hero.service.ts +:marked + We provide the `HeroService` in the application root `AppComponent` + so that is available everywhere in the app. + + Now it's time for some surgery to bring these files and the rest of the app + into alignment with our application router. + + ### New route definition with route parameter + + The new Heroes feature has two interacting components, the list and the detail. + The list view is self-sufficient; we navigate to it, it gets a list of heroes and displays them. + It doesn't need any outside information. + + The detail view is different. It displays a particular hero. It can't know which hero on its own. + That information must come from outside. + + In our example, when the user selects a hero from the list, we navigate to the detail view to show that hero. + We'll tell the detail view which hero to display by including the selected hero's id in the route URL. + + With that plan in mind, we return to the `app.component.ts` to make changes to the router's configuration + + First, we import the two components from their new locations in the `app/heroes/` folder: ++makeExample('router-deprecated/ts/app/app.component.2.ts','hero-import')(format=".") +:marked + Then we update the `@RouteConfig` route definitions : ++makeExample('router-deprecated/ts/app/app.component.2.ts','route-config')(format=".") +:marked + The `CrisisCenter` and `Heroes` definitions didn't change. + While we moved `hero-list.component.ts` to a new location in the `app/heroes/` folder, that only affects the `import` statement; + it doesn't affect its route definition. + + We added a new route definition for the `HeroDetailComponent` — and this definition has a twist. ++makeExample('router-deprecated/ts/app/app.component.2.ts','hero-detail-route')(format=".") +:marked + Notice the `:id` token in the path. That creates a slot in the path for a **Route Parameter**. + In this case, we're expecting the router to insert the `id` of a hero into that slot. + + If we tell the router to navigate to the detail component and display "Magneta", we expect hero `id` (15) to appear in the + browser URL like this: +code-example(format="." language="bash"). + localhost:3000/hero/15 +:marked + If a user enters that URL into the browser address bar, the router should recognize the + pattern and go to the same "Magneta" detail view. +.l-sub-section + :marked + #### Route parameter or query parameter? + Embedding the route parameter token, `:id`, in the route definition path is a good choice for our scenario + because the `id` is *required* by the `HeroDetailComponent` and because + the value `15` in the path clearly distinguishes the route to "Magneta" from + a route for some other hero. + + A [query parameter](#query-parameter) might be a better choice if we were passing an *optional* value to `HeroDetailComponent`. + + +:marked + ### Navigate to the detail imperatively + + *We don't navigate to the detail component by clicking a link*. + We won't be adding a new anchor tag to the shell navigation bar. + + Instead, we'll *detect* when the user selects a hero from the list and *command* the router + to present the hero detail view of the selected hero. + + We'll adjust the `HeroListComponent` to implement these tasks, beginning with its constructor + which acquires the router service and the `HeroService` by dependency injection: ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts','ctor')(format=".") +:marked + We make a few changes to the template: ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts','template')(format=".") +:marked + The template defines an `*ngFor` repeater such as [we've seen before](displaying-data.html#ngFor). + There's a `(click)` [EventBinding](template-syntax.html#event-binding) to the component's `onSelect` method + which we implement as follows: ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts','select')(format=".") +:marked + It calls the router's **`navigate`** method with a **Link Parameters Array**. + This array is similar to the *link parameters array* we met [earlier](#shell-template) in an anchor tag while + binding to the `RouterLink` directive. This time we see it in code rather than in HTML. + + + ### Setting the route parameters object + + We're navigating to the `HeroDetailComponent` where we expect to see the details of the selected hero. + We'll need *two* pieces of information: the destination and the hero's `id`. + + Accordingly, the *link parameters array* has *two* items: the **name** of the destination route and a **route parameters object** that specifies the + `id` of the selected hero. ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".") +:marked + The router composes the appropriate two-part destination URL from this array: +code-example(format="." language="bash"). + localhost:3000/hero/15 +:marked + ### Getting the route parameter + + + How does the target `HeroDetailComponent` learn about that `id`? + Certainly not by analyzing the URL! That's the router's job. + + The router extracts the route parameter (`id:15`) from the URL and supplies it to + the `HeroDetailComponent` via the **RouteParams** service. + + As usual, we write a constructor that asks Angular to inject that service among the other services + that the component require and reference them as private variables. ++makeExample('router-deprecated/ts/app/heroes/hero-detail.component.1.ts','ctor')(format=".") +:marked + Later, in the `ngOnInit` method, + we ask the `RouteParams` service for the `id` parameter by name and + tell the `HeroService` to fetch the hero with that `id`. ++makeExample('router-deprecated/ts/app/heroes/hero-detail.component.1.ts','ngOnInit')(format=".") + +.l-sub-section + :marked + Angular calls the `ngOnInit` method shortly after creating an instance of the `HeroDetailComponent`. + + We put the data access logic in the `ngOnInit` method rather than inside the constructor + to improve the component's testability. + We explore this point in greater detail in the [OnInit appendix](#onInit) below. +:marked + ### Navigating back to the list component + The `HeroDetailComponent` has a "Back" button wired to its `gotoHeroes` method that navigates imperatively + back to the `HeroListComponent`. + + The router `navigate` method takes the same one-item *link parameters array* + that we wrote for the `[routerLink]` directive binding. + It holds the **name of the `HeroListComponent` route**: ++makeExample('router-deprecated/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".") +:marked + ### Heroes App Wrap-up + + We've reached the second milestone in our router education. + + We've learned how to + * organize our app into *feature areas* + * navigate imperatively from one component to another + * pass information along in route parameters (`RouteParams`) + + After these changes, the folder structure looks like this: +.filetree + .file router-sample + .children + .file app + .children + .file heroes + .children + .file hero-detail.component.ts + .file hero-list.component.ts + .file hero.service.ts + .file app.component.ts + .file crisis-list.component.ts + .file main.ts + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file tsconfig.json + .file typings.json +:marked + + ### The Heroes App code + Here are the relevant files for this version of the sample application. ++makeTabs( + `router/ts/app/app.component.2.ts, + router/ts/app/heroes/hero-list.component.1.ts, + router/ts/app/heroes/hero-detail.component.1.ts, + router/ts/app/heroes/hero.service.ts`, + null, + `app.component.ts, + hero-list.component.ts, + hero-detail.component.ts, + hero.service.ts`) +:marked + + +.l-main-section +:marked + ## Milestone #3: The Crisis Center + The *Crisis Center* is a fake view at the moment. Time to make it useful. + + The new *Crisis Center* begins as a virtual copy of the *Heroes* feature. + We create a new `app/crisis-center` folder, copy the Hero files, + and change every mention of "hero" to "crisis". + + A `Crisis` has an `id` and `name`, just like a `Hero` + The new `CrisisListComponent` displays lists of crises. + When the user selects a crisis, the app navigates to the `CrisisDetailComponent` + for display and editing of the crisis name. + + VoilĂ , instant feature module! + + There's no point to this exercise unless we can learn something. + We do have new ideas and techniques in mind: + + * The application should navigate to the *Crisis Center* by default. + + * The user should be able to cancel unwanted changes. + + * The router should prevent navigation away from the detail view while there are pending changes. + + There are also a few lingering annoyances in the *Heroes* implementation that we can cure in the *Crisis Center*. + + * We currently register every route of every view at the highest level of the application. + If we expand the *Crisis Center* with a 100 new views, we'll make 100 changes to the + `AppComponent` route configuration. If we rename a *Crisis Center* component or change a route definition, + we'll be changing the `AppComponent` too. + + * If we followed *Heroes* lead, we'd be adding the `CrisisService` to the providers in `app.component.ts`. + Then both `HeroService` and `CrisisService` would be available everywhere although + they're only needed in their respective feature modules. That stinks. + + Changes to a sub-module such as *Crisis Center* shouldn't provoke changes to the `AppComponent` or `main.ts`. + We need to [*separate our concerns*](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html). + + We'll fix all of these problems and add the new routing features to *Crisis Center*. + + The most significant fix is the introduction of a **child *Routing Component*** + and its **child router** + + We'll leave *Heroes* in its less-than-perfect state to + serve as a contrast with what we hope is a superior *Crisis Center*. + + ### A free-standing Crisis Center Feature Module + The *Crisis Center* is one of two application workflows. + Users navigate between them depending on whether they are managing crises or heroes. + + The `CrisisCenter` and `Heroes` components are children of the root `AppComponent`. + + Unfortunately, they and their related files are physically commingled in the same folder with the `AppComponent`. + We'd prefer to separate them in their own *feature areas* so they can operate and evolve independently. + Someday we might re-use one or the other in a different application. + Someday we might load one of them dynamically only when the user chose to enter its workflow. + + Some might call it [yagni](http://martinfowler.com/bliki/Yagni.html) to even think about such things. + But we're right to be nervous about the way *Heroes* and *Crisis Center* artifacts are + bubbling up to the root `AppComponent` and blending with each other. + That's a [code smell](http://martinfowler.com/bliki/CodeSmell.html). + + Isolating feature area modules from each other looks good to us. +.l-sub-section + :marked + It's looking good as a general pattern for Angular applications. + figure.image-display + img(src='/resources/images/devguide/router/component-tree.png' alt="Component Tree" ) + :marked + * each feature area in its own module folder + * each area with its own root component + * each area root component with its own router-outlet and child routes + * area routes rarely (if ever) cross + +:marked + We'll make the *Crisis Center* stand on its own and leave the *Heroes* as it is + so we can compare the effort, results, and consequences. + Then each of us can decide which path to prefer (as if we didn't already know). + + + ### Child Routing Component + We create a new `app/crisis-center` folder and add `crisis-center.component.ts` to it with the following contents: ++makeExample('router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)') +:marked + The `CrisisCenterComponent` parallels the `AppComponent`. + + The `CrisisCenterComponent` is the root of the *Crisis Center* area + just as `AppComponent` is the root of the entire application. + + This `CrisisCenterComponent` is a shell for crisis management + just as the `AppComponent` is a shell to manage the high-level workflow. + + `AppComponent` has a `@RouteConfig` decorator that defines the top-level routes. + `CrisisCenterComponent` has a `@RouteConfig` decorator that defines *Crisis Center* child routes. + + The `CrisisCenterComponent` template is dead simple — simpler even than the `AppComponent` template. + It has no content, no links, just a `` for the *Crisis Center* child views. + + It has no selector either. It doesn't need one. We don't *embed* this component in a parent template. We *navigate* to it + from the outside, via a parent router (more on that soon). + + ### Service isolation + We add the `CrisisService` to the component's providers array + instead of registering it with the `bootstrap` function in `main.ts`. ++makeExample('router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts', 'providers') +:marked + This step limits the scope of that service to the *Crisis Center* component and its sub-component tree. + No component outside of the *Crisis Center* needs access to the `CrisisService`. + By restricting its scope, we feel confident that we can evolve it independently without fear of breaking + unrelated application modules — modules that *shouldn't have access to it anyway*. + + ### Child Route Configuration + The `CrisisCenterComponent` is a *Routing Component* like the `AppComponent`. + + The `@RouteConfig` decorator that adorns the `CrisisCenterComponent` class defines routes in much the same way + that we did earlier. ++makeExample('router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts', 'route-config', 'app/crisis-center/crisis-center.component.ts (routes only)' )(format=".") +:marked + The two routes terminate in the two *Crisis Center* child components, `CrisisListComponent` and `CrisisDetailComponent`. + + There is an *important difference* in the treatment of the root `AppComponent` paths and these paths. + Normally paths that begin with `/` refer to the root of the application. + Here they refer to the **root of the child component!**. + + The Component Router composes the final route by concatenating route paths beginning with the ancestor paths to this child router. + In our example, there is one ancestor path: "crisis-center". + The final route to the `CrisisDetailComponent` displaying the crisis whose `id` is 2 would be something like: +code-example(format=""). + localhost:3000/crisis-center/2 +:marked + We cannot know this simply by looking at the `CrisisCenterComponent` alone. + We can't tell that it is a *child* routing component. + We can't tell that its routes are child routes; they are indistinguiable from top level application routes. + + Such ignorance is intentional. The *Crisis Center* shouldn't know that it is the child of anything. + Today it is a child component one level down. + Tomorrow it might be the top level component of its own application. + Next month it might be re-purposed in a different application. + The *Crisis Center* itself is indifferent to these possibilities. + + *We* make it a child component of our application by reconfiguring the routes of the top level `AppComponent`. +:marked + ### Parent Route Configuration + Here is the revised route configuration for the parent `AppComponent`: ++makeExample('router-deprecated/ts/app/app.component.ts', 'route-config', 'app/app.component.ts (routes only)' ) +:marked + The last two *Hero* routes haven't changed. + + The first *Crisis Center* route has changed — *significantly* — and we've formatted it to draw attention to the differences: ++makeExample('router-deprecated/ts/app/app.component.ts', 'route-config-cc')(format=".") +:marked + Notice that the **path ends with a slash and three trailing periods (`/...`)**. + + That means this is an incomplete route (a ***non-terminal route***). The finished route will be some combination of + the parent `/crisis-center/` route and a route from the **child router** that belongs to the designated component. + + All is well. + The parent route's designated component is the `CrisisCenterComponent` which is a *Routing Component* with its own router and routes. + + + ### Default route + The other important change is the addition of the `useAsDefault` property. + Its value is `true` which makes *this* route the *default* route. + When the application launches, in the absence of any routing information from the browser's URL, the router + will default to the *Crisis Center*. That's our plan. + + ### Routing to the Child + + We've set the top level default route to go to the `CrisisCenterComponent`. + The final route will be a combination of `/crisis-center/` + and one of the child `CrisisCenterComponent` router's two routes. Which one? + + It could be either of them. In the absence of additional information, the router can't decide and must throw an error. + + We've tried the sample application and it didn't fail. We must have done something right. + + Look at the end of the child `CrisisCenterComponent`s first route. ++makeExample('router-deprecated/ts/app/crisis-center/crisis-center.component.1.ts', 'default-route', 'app/crisis-center/crisis-center.component.ts (default route)')(format=".") +:marked + We see `useAsDefault: true` once again. + That tells the router to compose the final URL using the path from the default *child* route. + Concatenate the base URL with the parent path, `/crisis-center/`, and the child path, `/`. + Remove superfluous slashes. We get: +code-example(format=""). + localhost:3000/crisis-center/ + +.l-main-section +:marked + + ## Router Lifecycle Hooks + + Angular components have [lifecycle hooks](lifecycle-hooks.html). For example, Angular calls the hook methods of the + [OnInit](../api/core/OnInit-interface.html) and [OnDestroy](../api/core/OnDestroy-interface.html) + interfaces when it creates and destroys components. + + The router also has hooks for *its* lifecycle such as + [CanActivate](../api/router/CanActivate-decorator.html), [OnActivate](../api/router/OnActivate-interface.html), and + [CanDeactivate](../api/router/CanDeactivate-interface.html). + These three hooks can change the way the router navigates *to* a component or *away* from a component. + + The router lifecycle hooks *supplement* the component lifecycle hooks. + We still need the component hooks but the router hooks do what the component hooks cannot. + For example, the component hooks can't stop component creation or destruction. + They can't pause view navigation to wait for an asynchronous process to finish because they are synchronous. + + A *router* hook can permit or prevent a navigation. + If the hook returns `true`, the navigation proceeds; if it returns `false`, the + router cancels the navigation and stays on the current view. + A hook can also tell the router to navigate to a *different* component. + + Router hook methods can act synchronously by returning a boolean value directly or + act asynchronously by returning a promise that resolves to a boolean. + + Let's look at `CanDeactivate`, one of the most important router hooks. +.l-sub-section + :marked + We'll examine other router hooks in a future update to this chapter. + +:marked + ### *CanDeactivate*: handling unsaved changes + + Back in the "Heroes" workflow, the app accepts every change to a hero immediately without hesitation or validation. + + In the real world, we might have to accumulate the users changes. + We might have to validate across fields. We might have to validate on the server. + We might have to hold changes in a pending state until the user confirms them *as a group* or + cancels and reverts all changes. + + What do we do about unapproved, unsaved changes when the user navigates away? + We can't just leave and risk losing the user's changes; that would be a terrible experience. + + We'd like to pause and let the user decide what to do. + If the user cancels, we'll stay put and allow more changes. + If the user approves, the app can save. + + We still might delay navigation until the save succeeds. + If we let the user move to the next screen immediately and + the save failed (perhaps the data are ruled invalid), we would have lost the context of the error. + + We can't block while waiting for the server — that's not possible in a browser. + We need to stop the navigation while we wait, asynchronously, for the server + to return with its answer. + + We need the `CanDeactivate` hook. + + ### Cancel and Save + + Our sample application doesn't talk to a server. + Fortunately, we have another way to demonstrate an asynchronous router hook. + + Users update crisis information in the `CrisisDetailComponent`. + Unlike the `HeroDetailComponent`, the user changes do not update the + crisis entity immediately. We update the entity when the user presses the *Save* button. + We discard the changes if the user presses he *Cancel* button. + + Both buttons navigate back to the crisis list after save or cancel. ++makeExample('router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts', 'cancel-save', 'crisis-detail.component.ts (excerpt)')(format=".") +:marked + What if the user tries to navigate away without saving or canceling? + The user could push the browser back button or click the heroes link. + Both actions trigger a navigation. + Should the app save or cancel automatically? + + We'll do neither. Instead we'll ask the user to make that choice explicitly + in a confirmation dialog box that *waits asynchronously for the user's + answer*. +.l-sub-section + :marked + We could wait for the user's answer with synchronous, blocking code. + Our app will be more responsive ... and can do other work ... + by waiting for the user's answer asynchronously. Waiting for the user asynchronously + is like waiting for the server asynchronously. +:marked + The `DialogService` (injected in the `AppComponent` for app-wide use) does the asking. + + It returns a [promise](http://exploringjs.com/es6/ch_promises.html) that + *resolves* when the user eventually decides what to do: either + to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`). + + + +:marked + We execute the dialog inside the router's `routerCanDeactivate` lifecycle hook method. ++makeExample('router-deprecated/ts/app/crisis-center/crisis-detail.component.1.ts', 'routerCanDeactivate', 'crisis-detail.component.ts (excerpt)') +:marked + Notice that the `routerCanDeactivate` 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 and the router will wait for that promise to resolve to truthy (navigate) or falsey (stay put). + + **Two critical points** + 1. The router hook is optional. We don't inherit from a base class. We simply implement the interface method or not. + + 1. We rely on the router to call the hook. We don't worry about all the ways that the user + could navigate away. That's the router's job. + We simply write this method and let the router take it from there. + + The relevant *Crisis Center* code for this milestone is + ++makeTabs( + `router/ts/app/crisis-center/crisis-center.component.ts, + router/ts/app/crisis-center/crisis-list.component.1.ts, + router/ts/app/crisis-center/crisis-detail.component.1.ts, + router/ts/app/crisis-center/crisis.service.ts + `, + null, + `crisis-center.component.ts, + crisis-list.component.ts, + crisis-detail.component.ts, + crisis.service.ts, + `) + + + + +.l-main-section +:marked + ## Milestone #4: Query Parameters + + We use [*route parameters*](#route-parameters) to specify a *required* parameterized value *within* the route URL + as we do when navigating to the `HeroDetailComponent` in order to view-and-edit the hero with *id:15*. +code-example(format="." language="bash"). + localhost:3000/hero/15 +:marked + Sometimes we wish to add *optional* information to a route request. + For example, the `HeroListComponent` doesn't need help to display a list of heroes. + But it might be nice if the previously-viewed hero were pre-selected when returning from the `HeroDetailComponent`. +figure.image-display + img(src='/resources/images/devguide/router/selected-hero.png' alt="Selected hero") +:marked + That becomes possible if we can include hero Magneta's `id` in the URL when we + return from the `HeroDetailComponent`, a scenario we'll pursue in a moment. + + Optional information takes other forms. Search criteria are often loosely structured, e.g., `name='wind*'`. + Multiple values are common — `after='12/31/2015' & before='1/1/2017'` — in no particular order — + `before='1/1/2017' & after='12/31/2015'` — in a variety of formats — `during='currentYear'` . + + These kinds of parameters don't fit easily in a URL *path*. Even if we could define a suitable URL token scheme, + doing so greatly complicates the pattern matching required to translate an incoming URL to a named route. + + The **URL query string** is the ideal vehicle for conveying arbitrarily complex information during navigation. + The query string isn't involved in pattern matching and affords enormous flexiblity of expression. + Almost anything serializable can appear in a query string. + + The Component Router supports navigation with query strings as well as route parameters. + We define query string parameters in the *route parameters object* just like we do with route parameters. + + + ### Route Parameters or Query Parameters? + + There is no hard-and-fast rule. In general, + + *prefer a route parameter when* + * the value is required. + * the value is necessary to distinguish one route path from another. + + *prefer a query parameter when* + * the value is optional. + * the value is complex and/or multi-variate. + + + ### Route parameters object + When navigating to the `HeroDetailComponent` we specified the `id` of the hero-to-edit in the + *route parameters object* and made it the second item of the [*link parameters array*](#link-parameters-array). + ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".") +:marked + The router embedded the `id` value in the navigation URL because we had defined it + as a route parameter with an `:id` placeholder token in the route `path`: ++makeExample('router-deprecated/ts/app/app.component.2.ts','hero-detail-route')(format=".") +:marked + When the user clicks the back button, the `HeroDetailComponent` constructs another *link parameters array* + which it uses to navigate back to the `HeroListComponent`. ++makeExample('router-deprecated/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".") +:marked + This array lacks a route parameters object because we had no reason to send information to the `HeroListComponent`. + + Now we have a reason. We'd like to send the id of the current hero with the navigation request so that the + `HeroListComponent` can highlight that hero in its list. + + We do that with a route parameters object in the same manner as before. + We also defined a junk parameter (`foo`) that the `HeroListComponent` should ignore. + Here's the revised navigation statement: ++makeExample('router-deprecated/ts/app/heroes/hero-detail.component.ts','gotoHeroes-navigate')(format=".") +:marked + The application still works. Clicking "back" returns to the hero list view. + + Look at the browser address bar. +.l-sub-section + img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px") + :marked + When running in plunker, pop out the preview window by clicking the blue 'X' button in the upper right corner. +:marked + It should look something like this, depending on where you run it: +code-example(format="." language="bash"). + localhost:3000/heroes?id=15&foo=foo +:marked + The `id` value appears in the query string (`?id=15&foo=foo`), not in the URL path. + The path for the "Heroes" route doesn't have an `:id` token. + +.alert.is-helpful + :marked + The router replaces route path tokens with corresponding values from the route parameters object. + **Every parameter _not_ consumed by a route path goes in the query string.** +:marked + ### Query parameters in the *RouteParams* service + + The list of heroes is unchanged. No hero row is highlighted. + +.l-sub-section + :marked + The [live example](/resources/live-examples/router/ts/plnkr.html) *does* highlight the selected + row because it demonstrates the final state of the application which includes the steps we're *about* to cover. + At the moment we're describing the state of affairs *prior* to those steps. +:marked + The `HeroListComponent` isn't expecting any parameters at all and wouldn't know what to do with them. + Let's change that. + + When navigating from the `HeroListComponent` to the `HeroDetailComponent` + the router picked up the route parameter object and made it available to the `HeroDetailComponent` + in the `RouteParams` service. We injected that service in the constructor of the `HeroDetailComponent`. + + This time we'll be navigating in the opposite direction, from the `HeroDetailComponent` to the `HeroListComponent`. + This time we'll inject the `RouteParams` service in the constructor of the `HeroListComponent`. + + First we extend the router import statement to include the `RouteParams` service symbol; ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.ts','import-route-params', 'hero-list.component.ts (import)')(format=".") +:marked + Then we extend the constructor to inject the `RouteParams` service and extract the `id` parameter as the `_selectedId`: ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.ts','ctor', 'hero-list.component.ts (constructor)')(format=".") +.l-sub-section + :marked + All route parameters are strings. + The (+) in front of the `routeParameters.get` expression is a JavaScript trick to convert the string to an integer. +:marked + We add an `isSelected` method that returns true when a hero's id matches the selected id. ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.ts','isSelected', 'hero-list.component.ts (constructor)')(format=".") +:marked + Finally, we update our template with a [Class Binding](template-syntax.html#class-binding) to that `isSelected` method. + The binding adds the `selected` CSS class when the method returns `true` and removes it when `false`. + Look for it within the repeated `
  • ` tag as shown here: ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.ts','template', 'hero-list.component.ts (template)')(format=".") +:marked + When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected: +figure.image-display + img(src='/resources/images/devguide/router/selected-hero.png' alt="Selected List" ) +:marked + The `foo` query string parameter is harmless and continues to be ignored. + + ### Child Routers and Query Parameters + + We can define query parameters for child routers too. + + The technique is precisely the same. + In fact, we made exactly the same changes to the *Crisis Center* feature. + Confirm the similarities in these *Hero* and *CrisisCenter* components, + arranged side-by-side for easy comparison: ++makeTabs( + `router/ts/app/heroes/hero-list.component.ts, + router/ts/app/crisis-center/crisis-list.component.ts, + router/ts/app/heroes/hero-detail.component.ts, + router/ts/app/crisis-center/crisis-detail.component.ts + `, + null, + `hero-list.component.ts, + crisis-list.component.ts, + hero-detail.component.ts, + crisis-detail.component.ts + `) +:marked + When we navigate back from a `CrisisDetailComponent` that is showing the *Asteroid* crisis, + we see that crisis properly selected in the list like this: +figure.image-display + img(src='/resources/images/devguide/router/selected-crisis.png' alt="Selected crisis" ) +:marked + **Look at the browser address bar again**. It's *different*. It looks something like this: +code-example(format="." language="bash"). + localhost:3000/crisis-center/;id=3;foo=foo +:marked + The query string parameters are no longer separated by "?" and "&". + They are **separated by semicolons (;)** + This is *matrix URL* notation — something we may not have seen before. +.l-sub-section + :marked + *Matrix URL* notation is an idea first floated + in a [1996 proposal](http://www.w3.org/DesignIssues/MatrixURIs.html) by the founder of the web, Tim Berners-Lee. + + Although matrix notation never made it into the HTML standard, it is legal and + it became popular among browser routing systems as a way to isolate parameters + belonging to parent and child routes. The Angular Component Router is such a system. + + The syntax may seem strange to us but users are unlikely to notice or care + as long as the URL can be emailed and pasted into a browser address bar + as this one can. + + + +.l-main-section +:marked + ## Wrap Up + As we end our chapter, we take a parting look at + the entire application. + + We can always try the [live example](/resources/live-examples/router/ts/plnkr.html) and download the source code from there. + + Our final project folder structure looks like this: +.filetree + .file router-sample + .children + .file app + .children + .file crisis-center/... + .file heroes/... + .file app.component.ts + .file dialog.service.ts + .file main.ts + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file tsconfig.json + .file typings.json +:marked + The pertinent top level application files are ++makeTabs( + `router/ts/app/app.component.ts, + router/ts/app/main.ts, + router/ts/app/dialog.service.ts, + router/ts/index.html + `, + null, + `app.component.ts, + main.ts, + dialog.service.ts, + index.html + `) +:marked + + ### Crisis Center + The *Crisis Center* feature area within the `crisis-center` folder follows: +.filetree + .file app + .children + .file crisis-center + .children + .file crisis-center.component.ts + .file crisis-detail.component.ts + .file crisis-list.component.ts + .file crisis.service.ts +:marked ++makeTabs( + `router/ts/app/crisis-center/crisis-center.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.service.ts + `, + null, + `crisis-center.component.ts, + crisis-list.component.ts, + crisis-detail.component.ts, + crisis.service.ts, + `) +:marked + ### Heroes + The *Heroes* feature area within the `heroes` folder is next: +.filetree + .file app + .children + .file heroes + .children + .file hero-detail.component.ts + .file hero-list.component.ts + .file hero.service.ts +:marked ++makeTabs( + `router/ts/app/heroes/hero-list.component.ts, + router/ts/app/heroes/hero-detail.component.ts, + router/ts/app/heroes/hero.service.ts + `, + null, + `hero-list.component.ts, + hero-detail.component.ts, + hero.service.ts + `) +:marked + + +.l-main-section +:marked + ## Appendices + The balance of this chapter is a set of appendices that + elaborate some of the points we covered quickly above. + + The appendix material isn't essential. Continued reading is for the curious. + + +.l-main-section + +:marked + ## Link Parameters Array + We've mentioned the *Link Parameters Array* several times. We've used it several times. + + We've bound the `RouterLink` directive to such an array like this: ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'h-anchor')(format=".") +:marked + We've written a two element array when specifying a route parameter like this ++makeExample('router-deprecated/ts/app/heroes/hero-list.component.1.ts', 'nav-to-detail')(format=".") +:marked + These two examples cover our needs for an app with one level routing. + The moment we add a child router, such as the *Crisis Center*, we create new link array possibilities. + + Recall that we specified a default child route for *Crisis Center* so this simple `RouterLink` is fine. ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'cc-anchor-w-default')(format=".") +:marked + *If we had not specified a default route*, our single item array would fail + because we didn't tell the router which child route to use. ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'cc-anchor-fail')(format=".") +:marked + We'd need to write our anchor with a link array like this: ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'cc-anchor-no-default')(format=".") +:marked + Let's parse it out. + * The first item in the array identifies the parent route ('CrisisCenter'). + * There are no parameters for this parent route so we're done with it. + * There is no default for the child route so we need to pick one. + * We decide to go to the `CrisisListComponent` whose route name is 'CrisisList' + * So we add that 'CrisisList' as the second item in the array. + * Voila! `['CrisisCenter', 'CrisisList']`. + + Let's take it a step further. + This time we'll build a link parameters array that navigates from the root of the application + down to the "Dragon Crisis". + + * The first item in the array identifies the parent route ('CrisisCenter'). + * There are no parameters for this parent route so we're done with it. + * The second item identifies the child route for details about a particular crisis ('CrisisDetail'). + * The details child route requires an `id` route parameter + * We add `id` of the *Dragon Crisis* as the third item in the array (`{id:1}`) + + It looks like this! ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'Dragon-anchor')(format=".") +:marked + If we wanted to, we could redefine our `AppComponent` template with *Crisis Center* routes exclusively: ++makeExample('router-deprecated/ts/app/app.component.3.ts', 'template')(format=".") +:marked + In sum, we can write applications with one, two or more levels of routing. + The link parameters array affords the flexibility to represent any routing depth and + any legal sequence of route names and (optional) route parameter objects. + + +.l-main-section +:marked + ## Appendix: Why use an *ngOnInit* method + + We implemented an `ngOnInit` method in many of our Component classes. + We did so, for example, in the [HeroDetailComponent](#hero-detail-ctor). + We might have put the `ngOnInit` logic inside the constructor instead. We didn't for a reason. The reason is *testability*. + + A constructor that has major side-effects can be difficult to test because it starts doing things as soon as + we create a test instance. In this case, it might have made a request to a remote server, something it shouldn't + do under test. It may even be impossible to reach the server in the test environment. + + The better practice is to limit what the constructor can do. Mostly it should stash parameters in + local variables and perform simple instance configuration. + + Yet we want an instance of this class to get the hero data from the `HeroService` soon after it is created. + How do we ensure that happens if not in the constructor? + + Angular detects when a component has certain lifecycle methods like + [ngOnInit](../api/core/OnInit-interface.html) and + [ngOnDestroy](../api/core/OnDestroy-interface.html) and calls + them + at the appropriate moment. + + Angular will call `ngOnInit` when we navigate to the `HeroDetailComponent`, we'll get the `id` from the `RouteParams` + and ask the server for the hero with that `id`. + + We too can call that `ngOnInit` method in our tests if we wish ... after taking control of the injected + `HeroService` and (perhaps) mocking it. + + + +.l-main-section +:marked + ## Appendix: *LocationStrategy* and browser URL styles + + When the router navigates to a new component view, it updates the browser's location and history + with a URL for that view. + This is a strictly local URL. The browser shouldn't send this URL to the server + and should not reload the page. + + Modern HTML 5 browsers support + [history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries), + a technique that changes a browser's location and history without triggering a server page request. + The router can compose a "natural" URL that is indistinguishable from + one that would otherwise require a page load. + + Here's the *Crisis Center* URL in this "HTML 5 pushState" style: +code-example(format=".", language="bash"). + localhost:3002/crisis-center/ +:marked + Older browsers send page requests to the server when the location URL changes ... + unless the change occurs after a "#" (called the "hash"). + Routers can take advantage of this exception by composing in-application route + URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center* +code-example(format=".", language="bash"). + localhost:3002/src/#/crisis-center/ +:marked + The Angular Component Router supports both styles with two `LocationStrategy` providers: + 1. `PathLocationStrategy` - the default "HTML 5 pushState" style. + 1. `HashLocationStrategy` - the "hash URL" style. + + The router's `ROUTER_PROVIDERS` array sets the `LocationStrategy` to the `PathLocationStrategy`, + making it the default strategy. + We can switch to the `HashLocationStrategy` with an override during the bootstrapping process if we prefer it. +.l-sub-section + :marked + Learn about "providers" and the bootstrap process in the + [Dependency Injection chapter](dependency-injection#bootstrap) +:marked + ### Which Strategy is Best? + We must choose a strategy and we need to make the right call early in the project. + It won't be easy to change later once the application is in production + and there are lots of application URL references in the wild. + + Almost all Angular 2 projects should use the default HTML 5 style. + It produces URLs that are easier for users to understand. + And it preserves the option to do **server-side rendering** later. + + Rendering critical pages on the server is a technique that can greatly improve + perceived responsiveness when the app first loads. + An app that would otherwise take ten or more seconds to start + could be rendered on the server and delivered to the user's device + in less than a second. + + This option is only available if application URLs look like normal web URLs + without hashes (#) in the middle. + + Stick with the default unless you have a compelling reason to + resort to hash routes. + + ### HTML 5 URLs and the *<base href>* + While the router uses the "[HTML 5 pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries)" + style by default, we *must* configure that strategy with a **base href** + + The preferred way to configure the strategy is to add a + [<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag + in the `` of the `index.html`. ++makeExample('router-deprecated/ts/index.1.html','base-href')(format=".") +:marked + Without that tag, the browser may not be able to load resources + (images, css, scripts) when "deep linking" into the app. + Bad things could happen when someone pastes an application link into the + browser's address bar or clicks such a link in an email link. + + Some developers may not be able to add the `` element, perhaps because they don't have + access to `` or the `index.html`. + + Those developers may still use HTML 5 URLs by taking two remedial steps: + + 1. Provide the router with an appropriate `APP_BASE_HREF` value. + 1. Use **absolute URLs** for all web resources: css, images, scripts, and template html files. + +.l-sub-section + :marked + Learn about the [APP_BASE_HREF](../api/router/APP_BASE_HREF-let.html) + in the API Guide. +:marked + ### *HashLocationStrategy* + We can go old-school with the `HashLocationStrategy` by + providing it as the router's `LocationStrategy` during application bootstrapping. + + First, import the `provide` symbol for Dependency Injection and the + `Location` and `HashLocationStrategy` symbols from the router. + + Then *override* the default strategy defined in `ROUTE_PROVIDERS` by + providing the `HashLocationStrategy` later in the `bootstrap` providers array argument: ++makeExample('router-deprecated/ts/app/main.2.ts','', 'main.ts (hash URL strategy)') diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index 29d2c0a12e..761a7223a0 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -1,4 +1,10 @@ include ../_util-fns +.alert.is-critical + :marked + This chapter is a *work in progress*. + + It describes the *release candidate* Component Router which + replaces the [*beta* router](router-deprecated.html). :marked The Angular ***Component Router*** enables navigation from one [view](./glossary.html#view) to the next @@ -65,7 +71,7 @@ include ../_util-fns If the `app` folder is the application root, as it is for our sample application, set the `href` value *exactly* as shown here. -+makeExample('router/ts/index.html','base-href', 'index.html (base href)')(format=".") ++makeExample('router/ts/index.1.html','base-href', 'index.html (base href)')(format=".") :marked ### Router imports @@ -299,7 +305,7 @@ figure.image-display If the `app` folder is the application root, as it is for our application, set the `href` value in **`index.html`** *exactly* as shown here. -+makeExample('router/ts/index.html','base-href', 'index.html (base href)')(format=".") ++makeExample('router/ts/index.1.html','base-href', 'index.html (base href)')(format=".") .l-sub-section :marked HTML 5 style navigation is the Component Router default. @@ -1443,7 +1449,7 @@ code-example(format=".", language="bash"). The preferred way to configure the strategy is to add a [<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag in the `` of the `index.html`. -+makeExample('router/ts/index.html','base-href')(format=".") ++makeExample('router/ts/index.1.html','base-href')(format=".") :marked Without that tag, the browser may not be able to load resources (images, css, scripts) when "deep linking" into the app.