docs(router): samples and doc for new router (phase 1)

This commit is contained in:
Ward Bell 2016-05-02 16:53:25 -07:00
parent f513ee83c7
commit 2ccdd867d2
69 changed files with 2954 additions and 491 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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": {

View File

@ -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');
}
});
}
});

View File

@ -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: `
<h1>Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
// #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

View File

@ -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: `
<h1>Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
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

View File

@ -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
<a [routerLink]="['Heroes']">Heroes</a>
// #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
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
// #enddocregion cc-anchor-w-default
// #enddocregion cc-anchor-fail
*/
/* Crisis Center link when CC lacks a default
// #docregion cc-anchor-no-default
<a [routerLink]="['CrisisCenter', 'CrisisList']">Crisis Center</a>
// #enddocregion cc-anchor-no-default
*/
/* Crisis Center Detail link
// #docregion Dragon-anchor
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Dragon Crisis</a>
// #enddocregion Dragon-anchor
*/
// #docregion template
template: `
<h1 class="title">Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter', 'CrisisList']">Crisis Center</a>
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Dragon Crisis</a>
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:2}]">Shark Crisis</a>
</nav>
<router-outlet></router-outlet>
`,
// #enddocregion template
providers: [DialogService, HeroService],
directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
{path: '/crisis-center/...', name: 'CrisisCenter', component: CrisisCenterComponent},
])
export class AppComponent { }

View File

@ -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: `
<h1 class="title">Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
// #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 { }

View File

@ -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: `
<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>
`,
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

View File

@ -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: `
<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>
`,
directives: [RouterOutlet],
providers: [CrisisService]
})
@RouteConfig([
{path:'/', name: 'CrisisList', component: CrisisListComponent, useAsDefault: true},
{path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent}
])
export class CrisisCenterComponent { }
// #enddocregion

View File

@ -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: `
<div *ngIf="crisis">
<h3>"{{editName}}"</h3>
<div>
<label>Id: </label>{{crisis.id}}</div>
<div>
<label>Name: </label>
<input [(ngModel)]="editName" placeholder="name"/>
</div>
<p>
<button (click)="save()">Save</button>
<button (click)="cancel()">Cancel</button>
</p>
</div>
`,
// #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 <a [routerLink]="['CrisisList']">Crisis Center</a
this._router.navigate(['CrisisList']);
}
// #enddocregion gotoCrises
// #docregion routerCanDeactivate, cancel-save
}
// #enddocregion routerCanDeactivate, cancel-save
// #enddocregion

View File

@ -0,0 +1,84 @@
// #docplaster
// #docregion
import {Component, OnInit} from '@angular/core';
import {Crisis, CrisisService} from './crisis.service';
import {RouteParams, Router} from '@angular/router-deprecated';
import {CanDeactivate, ComponentInstruction} from '@angular/router-deprecated';
import {DialogService} from '../dialog.service';
@Component({
template: `
<div *ngIf="crisis">
<h3>"{{editName}}"</h3>
<div>
<label>Id: </label>{{crisis.id}}</div>
<div>
<label>Name: </label>
<input [(ngModel)]="editName" placeholder="name"/>
</div>
<p>
<button (click)="save()">Save</button>
<button (click)="cancel()">Cancel</button>
</p>
</div>
`,
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
}

View File

@ -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: `
<ul class="items">
<li *ngFor="let crisis of crises"
(click)="onSelect(crisis)">
<span class="badge">{{crisis.id}}</span> {{crisis.name}}
</li>
</ul>
`,
// #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
}

View File

@ -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: `
<ul class="items">
<li *ngFor="let crisis of crises"
[class.selected]="isSelected(crisis)"
(click)="onSelect(crisis)">
<span class="badge">{{crisis.id}}</span> {{crisis.name}}
</li>
</ul>
`,
})
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 }] );
}
}

View File

@ -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

View File

@ -0,0 +1,10 @@
// Initial empty version
// #docregion
import {Component} from '@angular/core';
@Component({
template: `
<h2>CRISIS CENTER</h2>
<p>Get your crisis here</p>`
})
export class CrisisListComponent { }

View File

@ -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<boolean>((resolve, reject) =>
resolve(window.confirm(message || 'Is it OK?')));
};
}

View File

@ -0,0 +1,10 @@
/// Initial empty version
// #docregion
import {Component} from '@angular/core';
@Component({
template: `
<h2>HEROES</h2>
<p>Get your heroes here</p>`
})
export class HeroListComponent { }

View File

@ -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: `
<h2>HEROES</h2>
<div *ngIf="hero">
<h3>"{{hero.name}}"</h3>
<div>
<label>Id: </label>{{hero.id}}</div>
<div>
<label>Name: </label>
<input [(ngModel)]="hero.name" placeholder="name"/>
</div>
<p>
<button (click)="gotoHeroes()">Back</button>
</p>
</div>
`,
})
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 <a [routerLink]="['Heroes']">Heroes</a>
this._router.navigate(['Heroes']);
}
// #enddocregion gotoHeroes
}

View File

@ -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: `
<h2>HEROES</h2>
<div *ngIf="hero">
<h3>"{{hero.name}}"</h3>
<div>
<label>Id: </label>{{hero.id}}</div>
<div>
<label>Name: </label>
<input [(ngModel)]="hero.name" placeholder="name"/>
</div>
<p>
<button (click)="gotoHeroes()">Back</button>
</p>
</div>
`,
})
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
}

View File

@ -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: `
<h2>HEROES</h2>
<ul class="items">
<li *ngFor="let hero of heroes"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
`
// #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
*/

View File

@ -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: `
<h2>HEROES</h2>
<ul class="items">
<li *ngFor="let hero of heroes"
[class.selected]="isSelected(hero)"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
`
// #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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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]);

View File

@ -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]);

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<head>
<!-- #docregion base-href -->
<base href="/">
<!-- #enddocregion base-href -->
<title>Router Sample v.1</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app/main.1') // <----- ONLY CHANGE
.then(null, console.error.bind(console));
</script>
</head>
<body>
<h1>Milestone 1</h1>
<my-app>loading...</my-app>
</body>
</html>
<!-- #enddocregion -->

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<head>
<base href="/">
<title>Router Sample v.2</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app/main.2') // <----- ONLY CHANGE
.then(null, console.error.bind(console));
</script>
</head>
<body>
<h1>Milestone 2</h1>
<my-app>loading...</my-app>
</body>
</html>
<!-- #enddocregion -->

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<head>
<base href="/">
<title>Router Sample v.3</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app/main.3') // <----- ONLY CHANGE
.then(null, console.error.bind(console));
</script>
</head>
<body>
<h1>Milestone 3</h1>
<my-app>loading...</my-app>
</body>
</html>
<!-- #enddocregion -->

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<head>
<!-- Set the base href -->
<base href="/">
<title>Router Sample</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>loading...</my-app>
</body>
</html>
<!-- #enddocregion -->

View File

@ -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: `
<h1>Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
<a [routerLink]="['/crisis-center']">Crisis Center</a>
<a [routerLink]="['/heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
@ -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 { }

View File

@ -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: `
<h1>Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
<a [routerLink]="['/crisis-center']">Crisis Center</a>
<a [routerLink]="['/heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
@ -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

View File

@ -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
<a [routerLink]="['Heroes']">Heroes</a>
// #enddocregion h-anchor
*/
// #docregion h-anchor
<a [routerLink]="['/heroes']">Heroes</a>
// #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
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
// #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
<a [routerLink]="['/crisis-center']">Crisis Center</a>
// #enddocregion cc-anchor-w-default
// #enddocregion cc-anchor-fail
*/
/* Crisis Center link when CC lacks a default
// #docregion cc-anchor-no-default
<a [routerLink]="['CrisisCenter', 'CrisisList']">Crisis Center</a>
// #enddocregion cc-anchor-no-default
*/
// #docregion cc-anchor-no-default
<a [routerLink]="['/crisis-center/']">Crisis Center</a>
// #enddocregion cc-anchor-no-default
*/
/* Crisis Center Detail link
// #docregion Dragon-anchor
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Dragon Crisis</a>
// #enddocregion Dragon-anchor
*/
// #docregion Dragon-anchor
<a [routerLink]="['/crisis-center/1']">Dragon Crisis</a>
// #enddocregion Dragon-anchor
*/
// #docregion template
template: `
<h1 class="title">Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter', 'CrisisList']">Crisis Center</a>
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Dragon Crisis</a>
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:2}]">Shark Crisis</a>
<a [routerLink]="['/crisis-center']">Crisis Center</a>
<a [routerLink]="['/crisis-center', 1]">Dragon Crisis</a>
<a [routerLink]="['/crisis-center', 2]">Shark Crisis</a>
</nav>
<router-outlet></router-outlet>
`,
@ -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']);
}
}

View File

@ -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: `
<h1 class="title">Component Router</h1>
<nav>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
<a [routerLink]="['/crisis-center']">Crisis Center</a>
<a [routerLink]="['/heroes']">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
@ -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']);
}
}

View File

@ -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';
<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>
`,
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 { }

View File

@ -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: `
<h2>CRISIS CENTER</h2>
<router-outlet></router-outlet>
`,
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

View File

@ -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 <a [routerLink]="['CrisisList']">Crisis Center</a
this._router.navigate(['CrisisList']);
// Like <a [routerLink]="[/crisis-center/]">Crisis Center</a
this.router.navigateByUrl('/crisis-center/'); // absolute url
}
// #enddocregion gotoCrises
// #docregion routerCanDeactivate, cancel-save

View File

@ -1,11 +1,9 @@
// #docplaster
// #docregion
import {Component, OnInit} from '@angular/core';
import {Crisis, CrisisService} from './crisis.service';
import {RouteParams, Router} from '@angular/router-deprecated';
import {CanDeactivate, ComponentInstruction} from '@angular/router-deprecated';
import {DialogService} from '../dialog.service';
import { Component } from '@angular/core';
import { Crisis, CrisisService } from './crisis.service';
import { Router, OnActivate, CanDeactivate, RouteSegment } from '@angular/router';
import { DialogService } from '../dialog.service';
@Component({
template: `
@ -26,21 +24,22 @@ import {DialogService} from '../dialog.service';
styles: ['input {width: 20em}']
})
export class CrisisDetailComponent implements OnInit, CanDeactivate {
export class CrisisDetailComponent implements OnActivate, CanDeactivate {
crisis: Crisis;
editName: string;
private curSegment: RouteSegment;
constructor(
private _service: CrisisService,
private _router: Router,
private _routeParams: RouteParams,
private _dialog: DialogService
private service: CrisisService,
private router: Router,
private dialog: DialogService
) { }
ngOnInit() {
let id = +this._routeParams.get('id');
this._service.getCrisis(id).then(crisis => {
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

View File

@ -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
}

View File

@ -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';
</ul>
`,
})
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);
}
}

View File

@ -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

View File

@ -1,6 +1,6 @@
// Initial empty version
// #docregion
import {Component} from '@angular/core';
import { Component } from '@angular/core';
@Component({
template: `

View File

@ -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<boolean>((resolve, reject) =>
resolve(window.confirm(message || 'Is it OK?')));
};

View File

@ -1,6 +1,6 @@
/// Initial empty version
// #docregion
import {Component} from '@angular/core';
import { Component } from '@angular/core';
@Component({
template: `

View File

@ -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';
</div>
`,
})
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 <a [routerLink]="['Heroes']">Heroes</a>
this._router.navigate(['Heroes']);
// Like <a [routerLink]="['/heroes']">Heroes</a>
this.router.navigate(['/heroes']);
}
// #enddocregion gotoHeroes
}

View File

@ -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';
</div>
`,
})
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
}

View File

@ -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
*/
// #docregion link-parameters-array
['HeroDetail', { id: hero.id }] // {id: 15}
// #enddocregion link-parameters-array
*/

View File

@ -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

View File

@ -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);

View File

@ -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
// #enddocregion all

View File

@ -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

View File

@ -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]);

View File

@ -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]);

View File

@ -2,9 +2,7 @@
<!-- #docregion -->
<html>
<head>
<!-- #docregion base-href -->
<base href="/">
<!-- #enddocregion base-href -->
<title>Router Sample v.2</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -29,5 +27,10 @@
<my-app>loading...</my-app>
</body>
<body>
<h1>Milestone 2</h1>
<my-app>loading...</my-app>
</body>
</html>
<!-- #enddocregion -->

View File

@ -2,10 +2,8 @@
<!-- #docregion -->
<html>
<head>
<!-- #docregion base-href -->
<base href="/">
<!-- #enddocregion base-href -->
<title>Router Sample v.4</title>
<title>Router Sample v.3</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">

View File

@ -1,12 +1,10 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<html>
<head>
<!-- Set the base href -->
<!-- #docregion base-href -->
<base href="/">
<!-- #enddocregion base-href -->
<meta charset="UTF-8">
<title>Router Sample</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@ -27,8 +27,8 @@
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];

View File

@ -29,8 +29,8 @@
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];

View File

@ -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": {

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -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": {

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -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": {

View File

@ -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
<a id="chat-feature"></a>
.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;
}
```

File diff suppressed because it is too large Load Diff

View File

@ -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
[&lt;base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag
in the `<head>` 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.