docs(router): add query params milestone
Additional fixes suggested by recent issues. closes #637
This commit is contained in:
parent
8e3e0834b5
commit
af458737be
|
@ -6,11 +6,22 @@ import {Component} from 'angular2/core';
|
||||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||||
|
|
||||||
import {CrisisListComponent} from './crisis-list.component';
|
import {CrisisListComponent} from './crisis-list.component';
|
||||||
|
// #enddocregion
|
||||||
|
/*
|
||||||
|
// Apparent Milestone 2 imports
|
||||||
|
// #docregion
|
||||||
// #docregion hero-import
|
// #docregion hero-import
|
||||||
import {HeroListComponent} from './heroes/hero-list.component';
|
import {HeroListComponent} from './heroes/hero-list.component';
|
||||||
import {HeroDetailComponent} from './heroes/hero-detail.component';
|
import {HeroDetailComponent} from './heroes/hero-detail.component';
|
||||||
import {HeroService} from './heroes/hero.service';
|
import {HeroService} from './heroes/hero.service';
|
||||||
// #enddocregion hero-import
|
// #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({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
|
@ -45,6 +56,3 @@ import {HeroService} from './heroes/hero.service';
|
||||||
export class AppComponent { }
|
export class AppComponent { }
|
||||||
// #enddocregion route-config
|
// #enddocregion route-config
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
// #docregion child-router-link
|
|
||||||
// #enddocregion child-router-link
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
|
||||||
import {Component} from 'angular2/core';
|
import {Component} from 'angular2/core';
|
||||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||||
|
|
||||||
import {CrisisCenterComponent} from './crisis-center/crisis-center.component';
|
import {CrisisCenterComponent} from './crisis-center/crisis-center.component.1';
|
||||||
import {HeroListComponent} from './heroes/hero-list.component';
|
import {HeroListComponent} from './heroes/hero-list.component.1';
|
||||||
import {HeroDetailComponent} from './heroes/hero-detail.component';
|
import {HeroDetailComponent} from './heroes/hero-detail.component.1';
|
||||||
|
|
||||||
import {DialogService} from './dialog.service';
|
import {DialogService} from './dialog.service';
|
||||||
import {HeroService} from './heroes/hero.service';
|
import {HeroService} from './heroes/hero.service';
|
||||||
|
@ -36,7 +35,6 @@ import {HeroService} from './heroes/hero.service';
|
||||||
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Princess Crisis</a>
|
<a [routerLink]="['CrisisCenter', 'CrisisDetail', {id:1}]">Princess Crisis</a>
|
||||||
// #enddocregion princess-anchor
|
// #enddocregion princess-anchor
|
||||||
*/
|
*/
|
||||||
// #docregion
|
|
||||||
// #docregion template
|
// #docregion template
|
||||||
template: `
|
template: `
|
||||||
<h1 class="title">Component Router</h1>
|
<h1 class="title">Component Router</h1>
|
||||||
|
|
|
@ -3,36 +3,30 @@
|
||||||
// Also includes digression on HashPathStrategy (not used in the final app)
|
// Also includes digression on HashPathStrategy (not used in the final app)
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
// #docregion v2
|
// #docregion
|
||||||
import {bootstrap} from 'angular2/platform/browser';
|
import {bootstrap} from 'angular2/platform/browser';
|
||||||
import {ROUTER_PROVIDERS} from 'angular2/router';
|
import {ROUTER_PROVIDERS} from 'angular2/router';
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
// #enddocregion v2
|
|
||||||
|
|
||||||
// Add these symbols to register a `LocationStrategy`
|
// Add these symbols to override the `LocationStrategy`
|
||||||
import {provide} from 'angular2/core';
|
import {provide} from 'angular2/core';
|
||||||
import {LocationStrategy,
|
import {LocationStrategy,
|
||||||
HashLocationStrategy} from 'angular2/router';
|
HashLocationStrategy} from 'angular2/router';
|
||||||
// #enddocregion hash-strategy
|
// #enddocregion
|
||||||
|
|
||||||
/* Can't use AppComponent ... but display as if we can
|
/* Can't use AppComponent ... but display as if we can
|
||||||
// #docregion v2, hash-strategy
|
// #docregion
|
||||||
|
|
||||||
bootstrap(AppComponent, [
|
bootstrap(AppComponent, [
|
||||||
// #enddocregion v2, hash-strategy
|
// #enddocregion
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Actually use the v.2 component
|
// Actually use the v.2 component
|
||||||
import {AppComponent as ac} from './app.component.2';
|
import {AppComponent as ac} from './app.component.2';
|
||||||
|
|
||||||
bootstrap(ac, [
|
bootstrap(ac, [
|
||||||
// #docregion v2, hash-strategy
|
// #docregion
|
||||||
ROUTER_PROVIDERS,
|
ROUTER_PROVIDERS,
|
||||||
// #enddocregion v2, hash-strategy
|
|
||||||
// #docregion hash-strategy
|
|
||||||
provide(LocationStrategy,
|
provide(LocationStrategy,
|
||||||
{useClass: HashLocationStrategy}) // ~/#/crisis-center/
|
{useClass: HashLocationStrategy}) // .../#/crisis-center/
|
||||||
// #enddocregion hash-strategy
|
|
||||||
// #docregion v2, hash-strategy
|
|
||||||
]);
|
]);
|
||||||
// #enddocregion v2, hash-strategy
|
// #enddocregion
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {Component} from 'angular2/core';
|
||||||
|
import {RouteConfig, RouterOutlet} from 'angular2/router';
|
||||||
|
|
||||||
|
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: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},
|
||||||
|
// #enddocregion default-route
|
||||||
|
{path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent}
|
||||||
|
])
|
||||||
|
// #enddocregion route-config
|
||||||
|
export class CrisisCenterComponent { }
|
||||||
|
// #enddocregion minus-imports
|
|
@ -6,25 +6,17 @@ import {CrisisListComponent} from './crisis-list.component';
|
||||||
import {CrisisDetailComponent} from './crisis-detail.component';
|
import {CrisisDetailComponent} from './crisis-detail.component';
|
||||||
import {CrisisService} from './crisis.service';
|
import {CrisisService} from './crisis.service';
|
||||||
|
|
||||||
// #docregion minus-imports
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<h2>CRISIS CENTER</h2>
|
<h2>CRISIS CENTER</h2>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
`,
|
`,
|
||||||
directives: [RouterOutlet],
|
directives: [RouterOutlet],
|
||||||
// #docregion providers
|
|
||||||
providers: [CrisisService]
|
providers: [CrisisService]
|
||||||
// #enddocregion providers
|
|
||||||
})
|
})
|
||||||
// #docregion route-config
|
|
||||||
@RouteConfig([
|
@RouteConfig([
|
||||||
// #docregion default-route
|
|
||||||
{path:'/', name: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},
|
{path:'/', name: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},
|
||||||
// #enddocregion default-route
|
|
||||||
{path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent}
|
{path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent}
|
||||||
])
|
])
|
||||||
// #enddocregion route-config
|
|
||||||
export class CrisisCenterComponent { }
|
export class CrisisCenterComponent { }
|
||||||
// #enddocregion minus-imports
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
// #docplaster
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
import {Component, OnInit} from 'angular2/core';
|
||||||
|
import {Crisis, CrisisService} from './crisis.service';
|
||||||
|
import {RouteParams, Router} from 'angular2/router';
|
||||||
|
// #docregion routerCanDeactivate
|
||||||
|
import {CanDeactivate, ComponentInstruction} from 'angular2/router';
|
||||||
|
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>
|
||||||
|
<button (click)="save()">Save</button>
|
||||||
|
<button (click)="cancel()">Cancel</button>
|
||||||
|
</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]="['CrisisCenter']">Crisis Center</a
|
||||||
|
this._router.navigate(['CrisisCenter']);
|
||||||
|
}
|
||||||
|
// #enddocregion gotoCrises
|
||||||
|
// #docregion routerCanDeactivate, cancel-save
|
||||||
|
}
|
||||||
|
// #enddocregion routerCanDeactivate, cancel-save
|
||||||
|
// #enddocregion
|
|
@ -4,14 +4,10 @@
|
||||||
import {Component, OnInit} from 'angular2/core';
|
import {Component, OnInit} from 'angular2/core';
|
||||||
import {Crisis, CrisisService} from './crisis.service';
|
import {Crisis, CrisisService} from './crisis.service';
|
||||||
import {RouteParams, Router} from 'angular2/router';
|
import {RouteParams, Router} from 'angular2/router';
|
||||||
// #docregion routerCanDeactivate
|
|
||||||
import {CanDeactivate, ComponentInstruction} from 'angular2/router';
|
import {CanDeactivate, ComponentInstruction} from 'angular2/router';
|
||||||
import {DialogService} from '../dialog.service';
|
import {DialogService} from '../dialog.service';
|
||||||
|
|
||||||
// #enddocregion routerCanDeactivate
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// #docregion template
|
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="crisis">
|
<div *ngIf="crisis">
|
||||||
<h3>"{{editName}}"</h3>
|
<h3>"{{editName}}"</h3>
|
||||||
|
@ -25,16 +21,14 @@ import {DialogService} from '../dialog.service';
|
||||||
<button (click)="cancel()">Cancel</button>
|
<button (click)="cancel()">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
// #enddocregion template
|
|
||||||
styles: ['input {width: 20em}']
|
styles: ['input {width: 20em}']
|
||||||
})
|
})
|
||||||
// #docregion routerCanDeactivate, cancel-save
|
|
||||||
export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||||
|
|
||||||
crisis: Crisis;
|
crisis: Crisis;
|
||||||
editName: string;
|
editName: string;
|
||||||
|
|
||||||
// #enddocregion routerCanDeactivate, cancel-save
|
|
||||||
constructor(
|
constructor(
|
||||||
private _service: CrisisService,
|
private _service: CrisisService,
|
||||||
private _router: Router,
|
private _router: Router,
|
||||||
|
@ -42,7 +36,6 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||||
private _dialog: DialogService
|
private _dialog: DialogService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
// #docregion ngOnInit
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
let id = +this._routeParams.get('id');
|
let id = +this._routeParams.get('id');
|
||||||
this._service.getCrisis(id).then(crisis => {
|
this._service.getCrisis(id).then(crisis => {
|
||||||
|
@ -54,9 +47,7 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// #enddocregion ngOnInit
|
|
||||||
|
|
||||||
// #docregion routerCanDeactivate
|
|
||||||
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any {
|
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) : any {
|
||||||
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged.
|
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged.
|
||||||
if (!this.crisis || this.crisis.name === this.editName) {
|
if (!this.crisis || this.crisis.name === this.editName) {
|
||||||
|
@ -66,9 +57,7 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||||
// promise which resolves to true or false when the user decides
|
// 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
|
|
||||||
|
|
||||||
// #docregion cancel-save
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.editName = this.crisis.name;
|
this.editName = this.crisis.name;
|
||||||
this.gotoCrises();
|
this.gotoCrises();
|
||||||
|
@ -78,19 +67,16 @@ export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||||
this.crisis.name = this.editName;
|
this.crisis.name = this.editName;
|
||||||
this.gotoCrises();
|
this.gotoCrises();
|
||||||
}
|
}
|
||||||
// #enddocregion cancel-save
|
|
||||||
|
|
||||||
// #docregion gotoCrises
|
// #docregion gotoCrises
|
||||||
gotoCrises() {
|
gotoCrises() {
|
||||||
let route =
|
let crisisId = this.crisis ? this.crisis.id : null;
|
||||||
// pass along the crisis id if available
|
// Pass along the hero id if available
|
||||||
// so that the CrisisList component can select that crisis
|
// so that the CrisisListComponent can select that hero.
|
||||||
['CrisisCenter', {id: this.crisis ? this.crisis.id : null} ]
|
// Add a totally useless `foo` parameter for kicks.
|
||||||
|
// #docregion gotoCrises-navigate
|
||||||
this._router.navigate(route);
|
this._router.navigate(['CrisisCenter', {id: crisisId, foo: 'foo'} ]);
|
||||||
|
// #enddocregion gotoCrises-navigate
|
||||||
}
|
}
|
||||||
// #enddocregion gotoCrises
|
// #enddocregion gotoCrises
|
||||||
// #docregion routerCanDeactivate, cancel-save
|
|
||||||
}
|
}
|
||||||
// #enddocregion routerCanDeactivate, cancel-save
|
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// #docplaster
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
import {Component, OnInit} from 'angular2/core';
|
||||||
|
import {Crisis, CrisisService} from './crisis.service';
|
||||||
|
import {Router} from 'angular2/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
// #docregion template
|
||||||
|
template: `
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="#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
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import {Router, RouteParams} from 'angular2/router';
|
||||||
})
|
})
|
||||||
export class CrisisListComponent implements OnInit {
|
export class CrisisListComponent implements OnInit {
|
||||||
crises: Crisis[];
|
crises: Crisis[];
|
||||||
// #docregion isSelected
|
|
||||||
private _selectedId: number;
|
private _selectedId: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -27,19 +27,14 @@ export class CrisisListComponent implements OnInit {
|
||||||
routeParams: RouteParams) {
|
routeParams: RouteParams) {
|
||||||
this._selectedId = +routeParams.get('id');
|
this._selectedId = +routeParams.get('id');
|
||||||
}
|
}
|
||||||
// #enddocregion isSelected
|
|
||||||
|
isSelected(crisis: Crisis) { return crisis.id === this._selectedId; }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._service.getCrises().then(crises => this.crises = crises);
|
this._service.getCrises().then(crises => this.crises = crises);
|
||||||
}
|
}
|
||||||
// #docregion isSelected
|
|
||||||
|
|
||||||
isSelected(crisis: Crisis) { return crisis.id === this._selectedId; }
|
|
||||||
// #enddocregion isSelected
|
|
||||||
|
|
||||||
// #docregion select
|
|
||||||
onSelect(crisis: Crisis) {
|
onSelect(crisis: Crisis) {
|
||||||
this._router.navigate( ['CrisisDetail', { id: crisis.id }] );
|
this._router.navigate( ['CrisisDetail', { id: crisis.id }] );
|
||||||
}
|
}
|
||||||
// #enddocregion select
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// #docregion
|
||||||
|
import {Component, OnInit} from 'angular2/core';
|
||||||
|
import {Hero, HeroService} from './hero.service';
|
||||||
|
import {RouteParams, Router} from 'angular2/router';
|
||||||
|
|
||||||
|
@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>
|
||||||
|
<button (click)="gotoHeroes()">Back</button>
|
||||||
|
</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
|
||||||
|
}
|
|
@ -37,8 +37,13 @@ export class HeroDetailComponent implements OnInit {
|
||||||
|
|
||||||
// #docregion gotoHeroes
|
// #docregion gotoHeroes
|
||||||
gotoHeroes() {
|
gotoHeroes() {
|
||||||
// <a [routerLink]="['Heroes']">Heroes</a>
|
let heroId = this.hero ? this.hero.id : null;
|
||||||
this._router.navigate(['Heroes']);
|
// 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
|
// #enddocregion gotoHeroes
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
// #docplaster
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
// TODO SOMEDAY: Feature Componetized like HeroCenter
|
||||||
|
import {Component, OnInit} from 'angular2/core';
|
||||||
|
import {Hero, HeroService} from './hero.service';
|
||||||
|
import {Router} from 'angular2/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
// #docregion template
|
||||||
|
template: `
|
||||||
|
<h2>HEROES</h2>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="#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
|
||||||
|
*/
|
|
@ -1,10 +1,12 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
|
// TODO SOMEDAY: Feature Componetized like CrisisCenter
|
||||||
// #docregion
|
// #docregion
|
||||||
// TODO SOMEDAY: Feature Componetized like HeroCenter
|
|
||||||
import {Component, OnInit} from 'angular2/core';
|
import {Component, OnInit} from 'angular2/core';
|
||||||
import {Hero, HeroService} from './hero.service';
|
import {Hero, HeroService} from './hero.service';
|
||||||
import {Router} from 'angular2/router';
|
// #docregion import-route-params
|
||||||
|
import {Router, RouteParams} from 'angular2/router';
|
||||||
|
// #enddocregion import-route-params
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// #docregion template
|
// #docregion template
|
||||||
|
@ -12,6 +14,7 @@ import {Router} from 'angular2/router';
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="#hero of heroes"
|
<li *ngFor="#hero of heroes"
|
||||||
|
[class.selected]="isSelected(hero)"
|
||||||
(click)="onSelect(hero)">
|
(click)="onSelect(hero)">
|
||||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||||
</li>
|
</li>
|
||||||
|
@ -21,29 +24,30 @@ import {Router} from 'angular2/router';
|
||||||
})
|
})
|
||||||
export class HeroListComponent implements OnInit {
|
export class HeroListComponent implements OnInit {
|
||||||
heroes: Hero[];
|
heroes: Hero[];
|
||||||
selectedHero: Hero;
|
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
|
private _selectedId: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private _service: HeroService,
|
||||||
private _router: Router,
|
private _router: Router,
|
||||||
private _service: HeroService) { }
|
routeParams: RouteParams) {
|
||||||
|
this._selectedId = +routeParams.get('id');
|
||||||
|
}
|
||||||
// #enddocregion ctor
|
// #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() {
|
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 }] );
|
|
||||||
// #enddocregion nav-to-detail
|
|
||||||
}
|
|
||||||
// #enddocregion select
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
/* A link parameters array
|
|
||||||
// #docregion link-parameters-array
|
|
||||||
['HeroDetail', { id: hero.id }] // {id: 15}
|
|
||||||
// #enddocregion link-parameters-array
|
|
||||||
*/
|
|
|
@ -1,7 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<script>
|
|
||||||
var boot = 'app/boot'+'.1'; // choices: '.1', '.2', '.3', ''
|
|
||||||
</script>
|
|
||||||
<!-- #docregion -->
|
<!-- #docregion -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
@ -19,26 +16,20 @@
|
||||||
<!-- #enddocregion router-lib -->
|
<!-- #enddocregion router-lib -->
|
||||||
<script>
|
<script>
|
||||||
System.config({
|
System.config({
|
||||||
packages: {
|
packages: {
|
||||||
app: {
|
app: {
|
||||||
format: 'register',
|
format: 'register',
|
||||||
defaultExtension: 'js'
|
defaultExtension: 'js'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
System.import('app/boot')
|
System.import('app/boot.1') // <----- ONLY CHANGE
|
||||||
.then(null, console.error.bind(console));
|
.then(null, console.error.bind(console));
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
System.config({
|
|
||||||
packages: {'app': {defaultExtension: 'js'}}
|
|
||||||
});
|
|
||||||
// window.boot is for our testing; you should just use 'app/boot'
|
|
||||||
System.import(window.boot||'app/boot').catch(console.log.bind(console));
|
|
||||||
</script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<h1>Milestone 1</h1>
|
||||||
<my-app>loading...</my-app>
|
<my-app>loading...</my-app>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- #docregion -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- #docregion base-href -->
|
||||||
|
<base href="/">
|
||||||
|
<!-- #enddocregion base-href -->
|
||||||
|
<title>Router Sample</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||||
|
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||||
|
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||||
|
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||||
|
<!-- #docregion router-lib -->
|
||||||
|
<script src="node_modules/angular2/bundles/router.dev.js"></script>
|
||||||
|
<!-- #enddocregion router-lib -->
|
||||||
|
<script>
|
||||||
|
System.config({
|
||||||
|
packages: {
|
||||||
|
app: {
|
||||||
|
format: 'register',
|
||||||
|
defaultExtension: 'js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
System.import('app/boot.2') // <----- ONLY CHANGE
|
||||||
|
.then(null, console.error.bind(console));
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Milestone 2</h1>
|
||||||
|
<my-app>loading...</my-app>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
<!-- #enddocregion -->
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- #docregion -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- #docregion base-href -->
|
||||||
|
<base href="/">
|
||||||
|
<!-- #enddocregion base-href -->
|
||||||
|
<title>Router Sample</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||||
|
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||||
|
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||||
|
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||||
|
<!-- #docregion router-lib -->
|
||||||
|
<script src="node_modules/angular2/bundles/router.dev.js"></script>
|
||||||
|
<!-- #enddocregion router-lib -->
|
||||||
|
<script>
|
||||||
|
System.config({
|
||||||
|
packages: {
|
||||||
|
app: {
|
||||||
|
format: 'register',
|
||||||
|
defaultExtension: 'js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
System.import('app/boot.3') // <----- ONLY CHANGE
|
||||||
|
.then(null, console.error.bind(console));
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Milestone 3</h1>
|
||||||
|
<my-app>loading...</my-app>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
<!-- #enddocregion -->
|
|
@ -20,7 +20,9 @@ a:hover {color: white; background-color: #1171a3; }
|
||||||
a.router-link-active {color: white; background-color: #52b9e9; }
|
a.router-link-active {color: white; background-color: #52b9e9; }
|
||||||
|
|
||||||
/* #enddocregion starter */
|
/* #enddocregion starter */
|
||||||
|
/* #docregion selected */
|
||||||
.selected { background-color: #EEE; color: #369; }
|
.selected { background-color: #EEE; color: #369; }
|
||||||
|
/* #enddocregion selected */
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
|
|
@ -19,15 +19,52 @@ include ../../../../_includes/_util-fns
|
||||||
We can navigate imperatively when the user clicks a button, selects from a drop box,
|
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
|
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.
|
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
|
||||||
|
|
||||||
|
* [configuring a router](#route-config)
|
||||||
|
* the [link parameter arrays](#link-parameters-array) that propel 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)
|
||||||
|
|
||||||
|
We proceed in phases marked by milestones building rom a simple two-pager with placeholder views
|
||||||
|
up to a modular, multi-view design with child routes.
|
||||||
|
|
||||||
|
Try that [live final version](/resources/live-examples/router/ts/plnkr.html).
|
||||||
|
|
||||||
|
But first, an overview of router basics.
|
||||||
|
|
||||||
[Live Example](/resources/live-examples/router/ts/plnkr.html).
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## The Basics
|
## The Basics
|
||||||
Let's begin with a few core concepts of the Component Router.
|
Let's begin with a few core concepts of the Component Router.
|
||||||
Then we can explore the details through a sequence of examples.
|
Then we can explore the details through a sequence of examples.
|
||||||
|
### Setup
|
||||||
|
The 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
|
||||||
|
within the Angular npm bundle.
|
||||||
|
We make it available by loading its script in our `index.html`, after
|
||||||
|
the Angular core script.
|
||||||
|
+makeExample('router/ts/index.html','router-lib')(format=".")
|
||||||
|
:marked
|
||||||
|
### *<base href>*
|
||||||
|
Most routing applications should add a `<base>` element just after the `<head>` tag
|
||||||
|
to tell the router how to compose navigation URLs.
|
||||||
|
|
||||||
The **`Router`** is a service that presents a particular Component view for a given URL.
|
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')(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`**
|
When the browser's URL changes, the router looks for a corresponding **`RouteDefinition`**
|
||||||
from which it can determine the Component to display.
|
from which it can determine the Component to display.
|
||||||
|
|
||||||
|
@ -51,6 +88,7 @@ include ../../../../_includes/_util-fns
|
||||||
will use that value to find and present the hero whose `id` is 42.
|
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.
|
We'll learn more about route parameters later in this chapter.
|
||||||
:marked
|
:marked
|
||||||
|
### Router Outlet
|
||||||
Now we know how the router gets its configuration.
|
Now we know how the router gets its configuration.
|
||||||
When the browser URL for this application becomes `/heroes`,
|
When the browser URL for this application becomes `/heroes`,
|
||||||
the router matches that URL to the `RouteDefintion` named *Heroes* and displays the `HeroListComponent`
|
the router matches that URL to the `RouteDefintion` named *Heroes* and displays the `HeroListComponent`
|
||||||
|
@ -59,6 +97,7 @@ code-example(format="", language="html").
|
||||||
<!-- Routed views go here -->
|
<!-- Routed views go here -->
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
:marked
|
:marked
|
||||||
|
### Router Links
|
||||||
Now we have routes configured and a place to render them, but
|
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.
|
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
|
But most of the time we navigate as a result of some user action such as the click of
|
||||||
|
@ -77,7 +116,7 @@ code-example(format="", language="html").
|
||||||
'CrisisCenter' and 'Heroes' are the names of the `Routes` we configured above.
|
'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 —
|
We'll learn to write more complex link expressions — and why they are arrays —
|
||||||
[later](#link-parameter-array) in the chapter.
|
[later](#link-parameters-array) in the chapter.
|
||||||
:marked
|
:marked
|
||||||
### Let's summarize
|
### Let's summarize
|
||||||
|
|
||||||
|
@ -131,38 +170,25 @@ table
|
||||||
td.
|
td.
|
||||||
An Angular component with an attached router.
|
An Angular component with an attached router.
|
||||||
:marked
|
:marked
|
||||||
We'll learn many more details in this chapter which covers
|
We've barely touched the surface of the router and its capabilities.
|
||||||
|
|
||||||
* [configuring a router](#route-config)
|
The following detail sections describe a sample routing application
|
||||||
* the [link parameter arrays](#link-parameters-array) that propel router navigation
|
as it evolves over a sequence of milestones.
|
||||||
* navigating when the user clicks a data-bound [RouterLink](#router-link)
|
We strongly recommend taking the time to read and understand this story.
|
||||||
* navigating under [program control](#navigate)
|
|
||||||
* passing information in [route parameters](#route-parameter)
|
|
||||||
* creating a [child router](#child-router) with its own routes
|
|
||||||
* setting a [default route](#default)
|
|
||||||
* pausing, confirming and/or canceling a navigation with the the `routerCanDeactivate` [router lifecycle hook](#lifecycle-hooks)
|
|
||||||
|
|
||||||
We proceed in phases marked by milestones.
|
|
||||||
Our first milestone is the ability to navigate between two placeholder views.
|
|
||||||
At our last milestone, we'll have a modular, multi-view design with child routes.
|
|
||||||
|
|
||||||
We assume that you're already comfortable with the basic Angular 2 tools and concepts
|
|
||||||
we introduced in the [QuickStart](../quickstart.html) and
|
|
||||||
the [Tour of Heroes](../tutorial/) tutorial.
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
While we make incremental progress on a 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
|
|
||||||
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## The Sample Application
|
## The Sample Application
|
||||||
We have an application in mind as we move from milestone to milestone.
|
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.
|
Our client is the Hero Employment Agency.
|
||||||
Heroes need work and The Agency finds Crises for them to solve.
|
Heroes need work and The Agency finds Crises for them to solve.
|
||||||
|
|
||||||
|
@ -245,6 +271,7 @@ figure.image-display
|
||||||
The Component Router library is part of the Angular npm bundle.
|
The Component Router library is part of the Angular npm bundle.
|
||||||
We make it available by loading its script in our `index.html`, right after
|
We make it available by loading its script in our `index.html`, right after
|
||||||
the Angular core script.
|
the Angular core script.
|
||||||
|
<a id="base-href"></a>
|
||||||
+makeExample('router/ts/index.html','router-lib')(format=".")
|
+makeExample('router/ts/index.html','router-lib')(format=".")
|
||||||
:marked
|
:marked
|
||||||
### Set the *<base href>*
|
### Set the *<base href>*
|
||||||
|
@ -503,13 +530,13 @@ figure.image-display
|
||||||
Notice the `:id` token in the the path. That creates a slot in the path for a **Route Parameter**.
|
Notice the `:id` token in the 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.
|
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 "Magenta", we expect hero `id` (15) to appear in the
|
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:
|
browser URL like this:
|
||||||
code-example(format="." language="bash").
|
code-example(format="." language="bash").
|
||||||
localhost:3000/hero/15
|
localhost:3000/hero/15
|
||||||
:marked
|
:marked
|
||||||
If a user enters that URL into the browser address bar, the router should recognize the
|
If a user enters that URL into the browser address bar, the router should recognize the
|
||||||
pattern and go to the same "Magenta" detail view.
|
pattern and go to the same "Magneta" detail view.
|
||||||
|
|
||||||
<a id="navigate"></id>
|
<a id="navigate"></id>
|
||||||
### Navigate to the detail imperatively
|
### Navigate to the detail imperatively
|
||||||
|
@ -522,28 +549,29 @@ code-example(format="." language="bash").
|
||||||
|
|
||||||
We'll adjust the `HeroListComponent` to implement these tasks, beginning with its constructor
|
We'll adjust the `HeroListComponent` to implement these tasks, beginning with its constructor
|
||||||
which acquires the router service and the `HeroService` by dependency injection:
|
which acquires the router service and the `HeroService` by dependency injection:
|
||||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','ctor')(format=".")
|
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','ctor')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We make a few changes to the template:
|
We make a few changes to the template:
|
||||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','template')(format=".")
|
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','template')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The template defines an `*ngFor` repeater such as [we've seen before](displaying-data.html#ngFor).
|
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
|
There's a `(click)` [EventBinding](template-syntax.html#event-binding) to the component's `onSelect` method
|
||||||
which we implement as follows:
|
which we implement as follows:
|
||||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','select')(format=".")
|
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','select')(format=".")
|
||||||
:marked
|
:marked
|
||||||
It calls the router's **`navigate`** method with a **Link Parameters Array**.
|
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
|
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.
|
binding to the `RouterLink` directive. This time we see it in code rather than in HTML.
|
||||||
<a id="route-parameter"></id>
|
|
||||||
### Setting the route parameter
|
<a id="route-parameters"></id>
|
||||||
|
### Setting the route parameters object
|
||||||
|
|
||||||
We're navigating to the `HeroDetailComponent` where we expect to see the details of the selected hero.
|
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`.
|
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
|
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.
|
`id` of the selected hero.
|
||||||
+makeExample('router/ts/app/heroes/hero-list.component.ts','link-parameters-array')(format=".")
|
+makeExample('router/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The router composes the appropriate two-part destination URL from this array:
|
The router composes the appropriate two-part destination URL from this array:
|
||||||
code-example(format="." language="bash").
|
code-example(format="." language="bash").
|
||||||
|
@ -560,12 +588,12 @@ code-example(format="." language="bash").
|
||||||
|
|
||||||
As usual, we write a constructor that asks Angular to inject that service among the other services
|
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.
|
that the component require and reference them as private variables.
|
||||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','ctor')(format=".")
|
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','ctor')(format=".")
|
||||||
:marked
|
:marked
|
||||||
Later, in the `ngOnInit` method,
|
Later, in the `ngOnInit` method,
|
||||||
we ask the `RouteParams` service for the `id` parameter by name and
|
we ask the `RouteParams` service for the `id` parameter by name and
|
||||||
tell the `HeroService` to fetch the hero with that `id`.
|
tell the `HeroService` to fetch the hero with that `id`.
|
||||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','ngOnInit')(format=".")
|
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','ngOnInit')(format=".")
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -582,7 +610,7 @@ code-example(format="." language="bash").
|
||||||
The router `navigate` method takes the same one-item *link parameters array*
|
The router `navigate` method takes the same one-item *link parameters array*
|
||||||
that we wrote for the `[routerLink]` directive binding.
|
that we wrote for the `[routerLink]` directive binding.
|
||||||
It holds the **name of the `HeroListComponent` route**:
|
It holds the **name of the `HeroListComponent` route**:
|
||||||
+makeExample('router/ts/app/heroes/hero-detail.component.ts','gotoHeroes')(format=".")
|
+makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".")
|
||||||
:marked
|
:marked
|
||||||
### Heroes App Wrap-up
|
### Heroes App Wrap-up
|
||||||
|
|
||||||
|
@ -618,13 +646,11 @@ code-example(format="." language="bash").
|
||||||
Here are the relevant files for this version of the sample application.
|
Here are the relevant files for this version of the sample application.
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`router/ts/app/app.component.2.ts,
|
`router/ts/app/app.component.2.ts,
|
||||||
router/ts/app/boot.2.ts,
|
router/ts/app/heroes/hero-list.component.1.ts,
|
||||||
router/ts/app/heroes/hero-list.component.ts,
|
router/ts/app/heroes/hero-detail.component.1.ts,
|
||||||
router/ts/app/heroes/hero-detail.component.ts,
|
|
||||||
router/ts/app/heroes/hero.service.ts`,
|
router/ts/app/heroes/hero.service.ts`,
|
||||||
`,v2,,,`,
|
null,
|
||||||
`app.component.ts,
|
`app.component.ts,
|
||||||
boot.ts,
|
|
||||||
hero-list.component.ts,
|
hero-list.component.ts,
|
||||||
hero-detail.component.ts,
|
hero-detail.component.ts,
|
||||||
hero.service.ts`)
|
hero.service.ts`)
|
||||||
|
@ -714,7 +740,7 @@ code-example(format="." language="bash").
|
||||||
<a id="child-router"></id>
|
<a id="child-router"></id>
|
||||||
### Child Routing Component
|
### Child Routing Component
|
||||||
We create a new `app/crisis-center` folder and add `crisis-center-component.ts` to it with the following contents:
|
We create a new `app/crisis-center` folder and add `crisis-center-component.ts` to it with the following contents:
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)')
|
+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)')
|
||||||
:marked
|
:marked
|
||||||
The `CrisisCenterComponent` parallels the `AppComponent`.
|
The `CrisisCenterComponent` parallels the `AppComponent`.
|
||||||
|
|
||||||
|
@ -736,7 +762,7 @@ code-example(format="." language="bash").
|
||||||
### Service isolation
|
### Service isolation
|
||||||
We add the `CrisisService` to the component's providers array
|
We add the `CrisisService` to the component's providers array
|
||||||
instead of registering it with the `bootstrap` function in `boot.ts`.
|
instead of registering it with the `bootstrap` function in `boot.ts`.
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'providers')
|
+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'providers')
|
||||||
:marked
|
:marked
|
||||||
This step limits the scope of that service to the *Crisis Center* component and its sub-component tree.
|
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`.
|
No component outside of the *Crisis Center* needs access to the `CrisisService`.
|
||||||
|
@ -748,7 +774,7 @@ code-example(format="." language="bash").
|
||||||
|
|
||||||
The `@RouteConfig` decorator that adorns the `CrisisCenterComponent` class defines routes in much the same way
|
The `@RouteConfig` decorator that adorns the `CrisisCenterComponent` class defines routes in much the same way
|
||||||
that we did earlier.
|
that we did earlier.
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'route-config', 'app/crisis-center/crisis-center.component.ts (routes only)' )(format=".")
|
+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'route-config', 'app/crisis-center/crisis-center.component.ts (routes only)' )(format=".")
|
||||||
:marked
|
:marked
|
||||||
There is an *important difference in the paths*. They both begin at `/`.
|
There is an *important difference in the paths*. They both begin at `/`.
|
||||||
Normally such paths would refer to the root of the application.
|
Normally such paths would refer to the root of the application.
|
||||||
|
@ -807,7 +833,7 @@ code-example(format="").
|
||||||
We've tried the sample application and it didn't fail. We must have done something right.
|
We've tried the sample application and it didn't fail. We must have done something right.
|
||||||
|
|
||||||
Scroll to the end of the child `CrisisCenterComponent`s first route.
|
Scroll to the end of the child `CrisisCenterComponent`s first route.
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'default-route', 'app/crisis-center/crisis-center.component.ts (default route)')(format=".")
|
+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'default-route', 'app/crisis-center/crisis-center.component.ts (default route)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
There is `useAsDefault: true` again. That tells the router to compose the final URL using the path from the default child route.
|
There is `useAsDefault: true` again. That tells the router to compose the final URL using the path from the default child route.
|
||||||
Concatenate the base URL with `/crisis-center/` and `/`, remove extraneous slashes, and we get:
|
Concatenate the base URL with `/crisis-center/` and `/`, remove extraneous slashes, and we get:
|
||||||
|
@ -872,7 +898,7 @@ code-example(format="").
|
||||||
We discard the changes if the user presses he *Cancel* button.
|
We discard the changes if the user presses he *Cancel* button.
|
||||||
|
|
||||||
Both buttons navigate back to the crisis list after save or cancel.
|
Both buttons navigate back to the crisis list after save or cancel.
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'cancel-save', 'crisis-detail.component.ts (excerpt)')(format=".")
|
+makeExample('router/ts/app/crisis-center/crisis-detail.component.1.ts', 'cancel-save', 'crisis-detail.component.ts (excerpt)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
What if the user tries to navigate away without saving or canceling?
|
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.
|
The user could push the browser back button or click the heroes link.
|
||||||
|
@ -899,7 +925,7 @@ code-example(format="").
|
||||||
<a id="routerCanDeactivate"></a>
|
<a id="routerCanDeactivate"></a>
|
||||||
:marked
|
:marked
|
||||||
We execute the dialog inside the router's `routerCanDeactivate` lifecycle hook method.
|
We execute the dialog inside the router's `routerCanDeactivate` lifecycle hook method.
|
||||||
+makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'routerCanDeactivate', 'crisis-detail.component.ts (excerpt)')
|
+makeExample('router/ts/app/crisis-center/crisis-detail.component.1.ts', 'routerCanDeactivate', 'crisis-detail.component.ts (excerpt)')
|
||||||
:marked
|
:marked
|
||||||
Notice that the `routerCanDeactivate` method *can* return synchronously;
|
Notice that the `routerCanDeactivate` method *can* return synchronously;
|
||||||
it returns `true` immediately if there is no crisis or there are no pending changes.
|
it returns `true` immediately if there is no crisis or there are no pending changes.
|
||||||
|
@ -912,8 +938,196 @@ code-example(format="").
|
||||||
could navigate away. That's the router's job.
|
could navigate away. That's the router's job.
|
||||||
We simply write this method and let the router take it from there.
|
We simply write this method and let the router take it from there.
|
||||||
|
|
||||||
The final code for the *Crisis Center* feature is [here](#crisis-center-structure-and-code).
|
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,
|
||||||
|
`)
|
||||||
|
|
||||||
|
|
||||||
|
<a id="query-parameters"></a>
|
||||||
|
.l-main-section
|
||||||
|
:marked
|
||||||
|
## Milestone #4: Query Parameters
|
||||||
|
|
||||||
|
We use [*route parameters*](#route-parameters) to specify a 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" width="175")
|
||||||
|
: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.
|
||||||
|
|
||||||
|
<a id="route-parameters-object"></a>
|
||||||
|
### 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/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/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/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/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. It should look something like this:
|
||||||
|
code-example(format="." language="bash").
|
||||||
|
localhost:3000/heroes?id=15&foo=foo
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
Unfortunately, the browser address bar does not change when running the live example in plunker.
|
||||||
|
|
||||||
|
You can take our word for it or
|
||||||
|
download the sample from within plunker, unzip it, and browse to `index.html`.
|
||||||
|
You may have to launch it in a local server such as
|
||||||
|
[http-server](https://www.npmjs.com/package/http-server) or [lite-server](https://github.com/johnpapa/lite-server).
|
||||||
|
: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/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/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/ts/app/heroes/hero-list.component.ts','isSelected', 'hero-list.component.ts (constructor)')(format=".")
|
||||||
|
:marked
|
||||||
|
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 `<li>` tag as shown here:
|
||||||
|
+makeExample('router/ts/app/heroes/hero-list.component.ts','template', 'hero-list.component.ts (template)')(format=".")
|
||||||
|
:marked
|
||||||
|
Finally, we add the `selected` class to our CSS styles
|
||||||
|
+makeExample('router/ts/styles.css','selected', 'styles.css (selected class)')(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" width="250")
|
||||||
|
: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.
|
||||||
|
|
||||||
|
|
||||||
<a id="final-app"></a>
|
<a id="final-app"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -1016,7 +1230,7 @@ code-example(format="").
|
||||||
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="link-parameter-array"></a>
|
<a id="link-parameters-array"></a>
|
||||||
:marked
|
:marked
|
||||||
## Link Parameters Array
|
## Link Parameters Array
|
||||||
We've mentioned the *Link Parameters Array* several times. We've used it several times.
|
We've mentioned the *Link Parameters Array* several times. We've used it several times.
|
||||||
|
@ -1025,7 +1239,7 @@ code-example(format="").
|
||||||
+makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".")
|
+makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We've written a two element array when specifying a route parameter like this
|
We've written a two element array when specifying a route parameter like this
|
||||||
+makeExample('router/ts/app/heroes/hero-list.component.ts', 'nav-to-detail')(format=".")
|
+makeExample('router/ts/app/heroes/hero-list.component.1.ts', 'nav-to-detail')(format=".")
|
||||||
:marked
|
:marked
|
||||||
These two examples cover our needs for an app with one level routing.
|
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.
|
The moment we add a child router, such as the *Crisis Center*, we create new link array possibilities.
|
||||||
|
@ -1121,15 +1335,9 @@ code-example(format="").
|
||||||
|
|
||||||
When the router navigates to a new component view, it updates the browser's location and history
|
When the router navigates to a new component view, it updates the browser's location and history
|
||||||
with a URL for that view.
|
with a URL for that view.
|
||||||
This is a strictly local URL. The browser shouldn't send a request to the server
|
This is a strictly local URL. The browser shouldn't send this URL to the server
|
||||||
and should not reload the page.
|
and should not reload the page.
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
We're talking now about the ***browser*** URL
|
|
||||||
**not** the *route* URL that we record in a `RouteDefinition`.
|
|
||||||
The browser URL is what we paste into the browser's **address bar**
|
|
||||||
and email to folks so they can deep-link into an application page.
|
|
||||||
:marked
|
|
||||||
Modern HTML 5 browsers support
|
Modern HTML 5 browsers support
|
||||||
[history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries),
|
[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.
|
a technique that changes a browser's location and history without triggering a server page request.
|
||||||
|
@ -1142,13 +1350,18 @@ code-example(format=".", language="bash").
|
||||||
:marked
|
:marked
|
||||||
Older browsers send page requests to the server when the location URL changes ...
|
Older browsers send page requests to the server when the location URL changes ...
|
||||||
unless the change occurs after a "#" (called the "hash").
|
unless the change occurs after a "#" (called the "hash").
|
||||||
Routers take advantage of this exception by composing in-application route
|
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*
|
URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center*
|
||||||
code-example(format=".", language="bash").
|
code-example(format=".", language="bash").
|
||||||
localhost:3002/src/#/crisis-center/
|
localhost:3002/src/#/crisis-center/
|
||||||
:marked
|
:marked
|
||||||
The Angular Component Router supports both styles.
|
The Angular Component Router supports both styles with two `LocationStrategy` providers:
|
||||||
We set our preference by providing a `LocationStrategy` during the bootstrapping process.
|
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
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn about "providers" and the bootstrap process in the
|
Learn about "providers" and the bootstrap process in the
|
||||||
|
@ -1176,11 +1389,10 @@ code-example(format=".", language="bash").
|
||||||
resort to hash routes.
|
resort to hash routes.
|
||||||
|
|
||||||
### HTML 5 URLs and the *<base href>*
|
### HTML 5 URLs and the *<base href>*
|
||||||
The router use the "[HTML 5 pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries)"
|
While the router use the "[HTML 5 pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries)"
|
||||||
style by default.
|
style by default, we *must* configure that strategy with a **base href**
|
||||||
We don't have to provide the router's `PathLocationStrategy` because it's loaded automatically.
|
|
||||||
|
The preferred way to configure the strategy is to add a
|
||||||
We *must* add a
|
|
||||||
[<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag
|
[<base href> element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag
|
||||||
in the `<head>` of the `index.html`.
|
in the `<head>` of the `index.html`.
|
||||||
+makeExample('router/ts/index.html','base-href')(format=".")
|
+makeExample('router/ts/index.html','base-href')(format=".")
|
||||||
|
@ -1200,14 +1412,16 @@ code-example(format=".", language="bash").
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn about the [APP_BASE_HREF](https://angular.io/docs/ts/latest/api/router/APP_BASE_HREF-const.html)
|
Learn about the [APP_BASE_HREF](https://angular.io/docs/ts/latest/api/router/APP_BASE_HREF-let.html)
|
||||||
in the API Guide.
|
in the API Guide.
|
||||||
:marked
|
:marked
|
||||||
### *HashLocationStrategy*
|
### *HashLocationStrategy*
|
||||||
We can go old-school with the `HashLocationStrategy` by
|
We can go old-school with the `HashLocationStrategy` by
|
||||||
providing it as the router's `LocationStrategy` during application bootstrapping.
|
providing it as the router's `LocationStrategy` during application bootstrapping.
|
||||||
|
|
||||||
That means importing `provide` for Dependency Injection and the
|
First, import the `provide` symbol for Dependency Injection and the
|
||||||
`Location` and `HashLocationStrategy` symbols from the router,
|
`Location` and `HashLocationStrategy` symbols from the router.
|
||||||
then providing that strategy in the call to `bootstrap`:
|
|
||||||
+makeExample('router/ts/app/boot.2.ts', 'hash-strategy')
|
Then *override* the default strategy defined in `ROUTE_PROVIDERS` by
|
||||||
|
providing the `HashLocationStrategy` later in the `bootstrap` providers array argument:
|
||||||
|
+makeExample('router/ts/app/boot.2.ts','', 'boot.ts (hash URL strategy)')
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Loading…
Reference in New Issue