docs(router): new chapter
Ward and Jeremy Todo: EXCLUDE Old router doc for reference while re-writing
|
@ -0,0 +1 @@
|
|||
**/*.js
|
|
@ -0,0 +1,39 @@
|
|||
/* First version */
|
||||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||
|
||||
import {CrisisListComponent} from './crisis-list.component';
|
||||
import {HeroListComponent} from './hero-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: `
|
||||
<h1>Component Router</h1>
|
||||
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
|
||||
<a [routerLink]="['Heroes']">Heroes</a>
|
||||
<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
|
|
@ -0,0 +1,43 @@
|
|||
/* Second Heroes version */
|
||||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||
|
||||
import {CrisisListComponent} from './crisis-list.component';
|
||||
// #docregion hero-import
|
||||
import {HeroListComponent} from './heroes/hero-list.component';
|
||||
import {HeroDetailComponent} from './heroes/hero-detail.component';
|
||||
// #enddocregion hero-import
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>Component Router</h1>
|
||||
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
|
||||
<a [routerLink]="['Heroes']">Heroes</a>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
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
|
|
@ -0,0 +1,39 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||
|
||||
import {CrisisCenterComponent} from './crisis-center/crisis-center.component';
|
||||
import {HeroListComponent} from './heroes/hero-list.component';
|
||||
import {HeroDetailComponent} from './heroes/hero-detail.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: `
|
||||
<h1 class="title">Component Router</h1>
|
||||
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
|
||||
<a [routerLink]="['Heroes']">Heroes</a>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
// #enddocregion template
|
||||
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},
|
||||
{path: '/*other', redirectTo: ['Heroes']},
|
||||
])
|
||||
// #enddocregion route-config
|
||||
export class AppComponent { }
|
|
@ -0,0 +1,23 @@
|
|||
/* First version */
|
||||
// #docplaster
|
||||
|
||||
// #docregion all
|
||||
import {AppComponent} from './app.component';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {ROUTER_PROVIDERS} from 'angular2/router';
|
||||
|
||||
// #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
|
|
@ -0,0 +1,47 @@
|
|||
/* Second version */
|
||||
// For Milestone #2
|
||||
// Also includes digression on HashPathStrategy (not used in the final app)
|
||||
// #docplaster
|
||||
|
||||
// #docregion v2
|
||||
// #docregion hash-strategy
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {ROUTER_PROVIDERS} from 'angular2/router';
|
||||
import {AppComponent} from './app.component';
|
||||
// #enddocregion hash-strategy
|
||||
import {HeroService} from './heroes/hero.service';
|
||||
// #enddocregion v2
|
||||
|
||||
// #docregion hash-strategy
|
||||
|
||||
// Add these symbols to register a `LocationStrategy`
|
||||
import {provide} from 'angular2/core';
|
||||
import {LocationStrategy,
|
||||
HashLocationStrategy} from 'angular2/router';
|
||||
// #enddocregion hash-strategy
|
||||
|
||||
/* Can't use AppComponent ... but display as if we can
|
||||
// #docregion v2,hash-strategy
|
||||
|
||||
bootstrap(AppComponent, [
|
||||
// #enddocregion v2,hash-strategy
|
||||
*/
|
||||
|
||||
// Actually use the v.2 component
|
||||
import {AppComponent as ac} from './app.component.2';
|
||||
|
||||
bootstrap(ac, [
|
||||
// #docregion hash-strategy
|
||||
|
||||
provide(LocationStrategy,
|
||||
{useClass: HashLocationStrategy}), // ~/src/#/crisis-center/
|
||||
|
||||
// #enddocregion hash-strategy
|
||||
// #docregion v2
|
||||
HeroService,
|
||||
// #docregion hash-strategy
|
||||
ROUTER_PROVIDERS
|
||||
]);
|
||||
// #enddocregion hash-strategy
|
||||
// #enddocregion v2
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// #docregion
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {ROUTER_PROVIDERS} from 'angular2/router';
|
||||
|
||||
import {AppComponent} from './app.component';
|
||||
import {DialogService} from './dialog.service';
|
||||
import {HeroService} from './heroes/hero.service';
|
||||
|
||||
bootstrap(AppComponent, [
|
||||
ROUTER_PROVIDERS,
|
||||
DialogService,
|
||||
HeroService
|
||||
]);
|
|
@ -0,0 +1,41 @@
|
|||
import {Component} from 'angular2/core';
|
||||
import {Crisis, CrisisService} from './crisis.service';
|
||||
import {DialogService} from '../dialog.service';
|
||||
import {CanDeactivate, ComponentInstruction, Router} from 'angular2/router';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<h2>"{{editName}}"</h2>
|
||||
<div>
|
||||
<label>Name: </label>
|
||||
<input [(ngModel)]="editName" placeholder="name"/>
|
||||
</div>
|
||||
<button (click)="save()">Save</button>
|
||||
<button (click)="cancel()">Cancel</button>
|
||||
`,
|
||||
styles: ['input {width: 20em}']
|
||||
})
|
||||
export class AddCrisisComponent implements CanDeactivate {
|
||||
public editName: string;
|
||||
|
||||
constructor(
|
||||
private _service: CrisisService,
|
||||
private _router: Router,
|
||||
private _dialog: DialogService) { }
|
||||
|
||||
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
return !!this.editName.trim() ||
|
||||
this._dialog.confirm('Discard changes?');
|
||||
}
|
||||
|
||||
cancel() { this.gotoCrises(); }
|
||||
|
||||
save() {
|
||||
this._service.addCrisis(this.editName);
|
||||
this.gotoCrises();
|
||||
}
|
||||
|
||||
gotoCrises() {
|
||||
this._router.navigate(['CrisisCenter']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {RouteConfig, RouterOutlet} from 'angular2/router';
|
||||
|
||||
import {CrisisListComponent} from './crisis-list.component';
|
||||
import {CrisisDetailComponent} from './crisis-detail.component';
|
||||
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([
|
||||
{path:'/', name: 'CrisisCenter', component: CrisisListComponent, useAsDefault: true},
|
||||
{path:'/list/:id', name: 'CrisisList', component: CrisisListComponent},
|
||||
{path:'/:id', name: 'CrisisDetail', component: CrisisDetailComponent}
|
||||
])
|
||||
// #enddocregion route-config
|
||||
export class CrisisCenterComponent { }
|
||||
// #enddocregion minus-imports
|
||||
// #enddocregion
|
|
@ -0,0 +1,90 @@
|
|||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
import {Component, OnInit} from 'angular2/core';
|
||||
import {Crisis, CrisisService} from './crisis.service';
|
||||
import {RouteParams, Router} from 'angular2/router';
|
||||
// #docregion ngCanDeactivate
|
||||
import {CanDeactivate, ComponentInstruction} from 'angular2/router';
|
||||
import {DialogService} from '../dialog.service';
|
||||
|
||||
// #enddocregion ngCanDeactivate
|
||||
|
||||
@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 ngCanDeactivate, cancel-save
|
||||
export class CrisisDetailComponent implements OnInit, CanDeactivate {
|
||||
|
||||
public crisis: Crisis;
|
||||
public editName: string;
|
||||
|
||||
// #enddocregion ngCanDeactivate, 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 canDeactivate
|
||||
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
return !this.crisis ||
|
||||
this.crisis.name === this.editName ||
|
||||
this._dialog.confirm('Discard changes?');
|
||||
}
|
||||
// #enddocregion canDeactivate
|
||||
|
||||
// #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() {
|
||||
let route =
|
||||
['CrisisList', {id: this.crisis ? this.crisis.id : null} ]
|
||||
|
||||
this._router.navigate(route);
|
||||
}
|
||||
// #enddocregion gotoCrises
|
||||
// #docregion ngCanDeactivate, cancel-save
|
||||
}
|
||||
// #enddocregion ngCanDeactivate, cancel-save
|
||||
// #enddocregion
|
|
@ -0,0 +1,45 @@
|
|||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
import {Component, OnInit} from 'angular2/core';
|
||||
import {Crisis, CrisisService} from './crisis.service';
|
||||
import {Router, RouteParams} from 'angular2/router';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="#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 {
|
||||
public crises: Crisis[];
|
||||
// #docregion isSelected
|
||||
private _selectedId: number;
|
||||
|
||||
constructor(
|
||||
private _service: CrisisService,
|
||||
private _router: Router,
|
||||
routeParams: RouteParams) {
|
||||
this._selectedId = +routeParams.get('id');
|
||||
}
|
||||
// #enddocregion isSelected
|
||||
|
||||
ngOnInit() {
|
||||
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) {
|
||||
this._router.navigate( ['CrisisDetail', { id: crisis.id }] );
|
||||
}
|
||||
// #enddocregion select
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// #docplaster
|
||||
|
||||
// #docregion
|
||||
import {Injectable} from 'angular2/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, 'Princess Held Captive'),
|
||||
new Crisis(2, 'Dragon Burning Cities'),
|
||||
new Crisis(3, 'Giant Asteroid Heading For Earth'),
|
||||
new Crisis(4, 'Release Deadline Looms')
|
||||
];
|
||||
|
||||
var crisesPromise = Promise.resolve(crises);
|
||||
// #enddocregion
|
|
@ -0,0 +1,10 @@
|
|||
// Initial empty version
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<h2>CRISIS CENTER</h2>
|
||||
<p>Get your crisis here</p>`
|
||||
})
|
||||
export class CrisisListComponent { }
|
|
@ -0,0 +1,18 @@
|
|||
// #docregion
|
||||
import {Injectable} from 'angular2/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?')));
|
||||
};
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/// Initial empty version
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<h2>HEROES</h2>
|
||||
<p>Get your heroes here</p>`
|
||||
})
|
||||
export class HeroListComponent { }
|
|
@ -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 {
|
||||
public 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() {
|
||||
// <a [routerLink]="['Heroes']">Heroes</a>
|
||||
this._router.navigate(['Heroes']);
|
||||
}
|
||||
// #enddocregion gotoHeroes
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// #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 {
|
||||
public heroes: Hero[];
|
||||
public selectedHero: 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) {
|
||||
this._router.navigate( ['HeroDetail', { id: hero.id }] );
|
||||
}
|
||||
// #enddocregion select
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
/* A link parameters array
|
||||
// #docregion link-parameters-array
|
||||
['HeroDetail', { id: hero.id }] // {id: 15}
|
||||
// #enddocregion link-parameters-array
|
||||
*/
|
|
@ -0,0 +1,27 @@
|
|||
// #docregion
|
||||
import {Injectable} from 'angular2/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);
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
var boot = 'app/boot'+'.2'; // choices: '.1', '.2', ''
|
||||
</script>
|
||||
<!-- #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/systemjs/dist/system.src.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': {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>
|
||||
|
||||
<body>
|
||||
<my-app>loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<!-- #enddocregion -->
|
|
@ -0,0 +1,29 @@
|
|||
<!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/systemjs/dist/system.src.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': {defaultExtension: 'js'}}
|
||||
});
|
||||
System.import('app/boot').catch(console.log.bind(console));
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<!-- #enddocregion -->
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"description": "Router",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2,3].*",
|
||||
"!app/crisis-list.component.ts",
|
||||
"!app/hero-list.component.ts",
|
||||
"!app/crisis-center/add-crisis.component.ts"
|
||||
],
|
||||
"tags": ["router"]
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* #docregion */
|
||||
/* #docregion heroes */
|
||||
/* #docregion starter */
|
||||
h1 {color: #369; font-family: Arial, Helvetica, sans-serif; font-size: 250%;}
|
||||
h2 { color: #369; font-family: Arial, Helvetica, sans-serif; }
|
||||
h3 { color: #444; font-weight: lighter; }
|
||||
body { margin: 2em; }
|
||||
body, input[text], button { color: #888; font-family: Cambria, Georgia; }
|
||||
button {padding: 0.2em; font-size: 14px}
|
||||
|
||||
ul {list-style-type: none; margin-left: 1em; padding: 0; width: 20em;}
|
||||
|
||||
li { cursor: pointer; position: relative; left: 0; transition: all 0.2s ease; }
|
||||
li:hover {color: #369; background-color: #EEE; left: .2em;}
|
||||
|
||||
/* route-link anchor tags */
|
||||
a {padding: 5px; text-decoration: none; font-family: Arial, Helvetica, sans-serif; }
|
||||
a:visited, a:link {color: #444;}
|
||||
a:hover {color: white; background-color: #1171a3; }
|
||||
a.router-link-active {color: white; background-color: #52b9e9; }
|
||||
|
||||
/* #enddocregion starter */
|
||||
.selected { background-color: #EEE; color: #369; }
|
||||
|
||||
.badge {
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.1em 0.7em;
|
||||
background-color: #369;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
/* #enddocregion heroes */
|
||||
/* #enddocregion */
|
|
@ -122,7 +122,7 @@ export class AppComponent {
|
|||
// #docregion setStyles2
|
||||
setStyles2() {
|
||||
return {
|
||||
// camelCase style properties works too
|
||||
// camelCase style properties work too
|
||||
fontStyle: this.canSave ? 'italic' : 'normal', // italic
|
||||
fontWeight: !this.isUnchanged ? 'bold' : 'normal', // normal
|
||||
fontSize: this.isSpecial ? 'x-large': 'smaller', // larger
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
"intro": "Pipes transform displayed values within a template"
|
||||
},
|
||||
|
||||
"router": {
|
||||
"title": "Routing & Navigation",
|
||||
"intro": "Discover the basics of screen navigation with the Angular 2 router"
|
||||
},
|
||||
|
||||
"attribute-directives": {
|
||||
"title": "Attribute Directives",
|
||||
"intro": "Attribute directives attach behavior to elements."
|
||||
|
|
|
@ -399,9 +399,21 @@
|
|||
and taking other similar actions that cause the application to
|
||||
replace one view with another.
|
||||
|
||||
The [Angular router](./router.html) is a richly featured mechanism for configuring
|
||||
and managing the entire navigation process including the creation and destruction
|
||||
The Angular [Component Router](./router.html) is a richly featured mechanism for configuring
|
||||
and managing the entire view navigation process including the creation and destruction
|
||||
of views.
|
||||
:marked
|
||||
## Routing Component
|
||||
.l-sub-section
|
||||
:marked
|
||||
A [Component](#component) with an attached router.
|
||||
|
||||
In most cases, the component became attached to a [router](#router) by means
|
||||
of a `@RouterConfig` decorator that defined routes to views controlled by this component.
|
||||
|
||||
The component's template has a `RouterOutlet` element where it can display views produced by the router.
|
||||
|
||||
It likely has anchor tags or buttons with `RouterLink` directives that users can click to navigate.
|
||||
|
||||
.l-main-section
|
||||
<a id="S"></a>
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
TODO: REVIVE AUX ROUTE MATERIAL WHEN THAT FEATURE WORKS AS EXPECTED
|
||||
|
||||
<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 Heros 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
|
||||
Heros 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;
|
||||
}
|
||||
```
|
|
@ -0,0 +1,724 @@
|
|||
.l-main-section
|
||||
|
||||
h2#section-route-use Using the Component Router
|
||||
p There are three steps to setting up routing with Angular's Component Router
|
||||
ol
|
||||
li Install the Component Router
|
||||
li Map paths to components
|
||||
li Link to routes
|
||||
|
||||
|
||||
.l-main-section
|
||||
|
||||
h2#section-install-router Import the Component Router
|
||||
|
||||
p.
|
||||
Create two files, <code>index.html</code> and <code>app.ts</code>, both at the root of the project:
|
||||
|
||||
pre.prettyprint.lang-bash
|
||||
code.
|
||||
touch index.html app.ts
|
||||
|
||||
p Your app directory should look something like:
|
||||
pre.prettyprint.lang-bash
|
||||
code.
|
||||
app.ts
|
||||
index.html
|
||||
package.json
|
||||
node_modules/
|
||||
└── ...
|
||||
|
||||
p.
|
||||
Because the component is an addition to the core, you must install Angular's Component Router into your app.
|
||||
When using the angular2.dev.js bundle you have include the additional router.dev.js bundle.
|
||||
|
||||
p.
|
||||
Add Angular and Component Router into your app by adding the relevant <code><script></code> tags into your
|
||||
<code>index.html</code>:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/">
|
||||
<title>My app</title>
|
||||
</head>
|
||||
<body ng-app="myApp" ng-controller="AppController as app">
|
||||
<div ng-outlet></div>
|
||||
<script src="/node_modules/angular/angular.js"></script>
|
||||
<script src="/dist/router.es5.js"></script>
|
||||
<script src="/app/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
pre.prettyprint.lang-html.is-angular2
|
||||
code.
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/">
|
||||
<title>My app</title>
|
||||
</head>
|
||||
<body ng-app="myApp" ng-controller="AppController as app">
|
||||
<script src="https://jspm.io/system@0.16.js"></script>
|
||||
<script src="https://code.angularjs.org/2.0.0-alpha.21/angular2.dev.js"></script>
|
||||
<script src="https://code.angularjs.org/2.0.0-alpha.21/router.dev.js"></script>
|
||||
<script>
|
||||
System.import('main');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
p.is-angular2.
|
||||
Then you can add the router into your app by importing the Router module in your <code>app.ts</code> file:
|
||||
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
import {Component, View, bootstrap} from 'angular2/angular2';
|
||||
import {Router, RouterOutlet, RouterLink} from 'angular2/router';
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
// self-executing bundle deploys angular APIs on the window object.
|
||||
window.angular;
|
||||
// the router APIs are part of the router sub-object.
|
||||
window.angular.router;
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
This is a pretty typical angular app, except the <code>ng-outlet</code> directive. <code>ng-outlet</code> is like
|
||||
<code>ng-view</code>; it's a placeholder for part of your app loaded dynamically based on the route configuration.
|
||||
|
||||
p.is-angular2.
|
||||
This is the same as you've seen in the rest of Angular 2, except the <code>router-outlet</code> directive.
|
||||
<code>router-outlet</code> is a placeholder for part of your app loaded dynamically based on the route configuration.
|
||||
|
||||
p So how do we configure the app? Let's open <code>app.ts</code> and find out. Add this to the file:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-javascript.is-angular1.is-hidden
|
||||
code.
|
||||
angular.module('app', ['ngNewRouter'])
|
||||
.controller('AppController', ['$router', AppController]);
|
||||
|
||||
AppController.$routeConfig = [
|
||||
{path: '/', component: 'home' }
|
||||
];
|
||||
function AppController ($router) {}
|
||||
|
||||
// ANGULAR 2
|
||||
.code-box
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
import {Component, View, bootstrap} from 'angular2/angular2';
|
||||
import {routerInjectables, RouterOutlet} from 'angular2/router';
|
||||
|
||||
import {HomeComp} from './components/home';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app'
|
||||
})
|
||||
@View({
|
||||
template: '<router-outlet></router-outlet>',
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@RouteConfig([
|
||||
{path: '/', component: HomeComp }
|
||||
])
|
||||
class AppComp {}
|
||||
|
||||
bootstrap(AppComp, routerInjectables);
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
var HomeComp = function() {};
|
||||
...
|
||||
|
||||
var AppComp = function() {};
|
||||
AppComp.annotations = [
|
||||
new angular.ComponentAnnotation({
|
||||
selector: 'my-app'
|
||||
}),
|
||||
new angular.ViewAnnotation({
|
||||
template: '<router-outlet></router-outlet>',
|
||||
directives: [angular.router.RouterOutlet]
|
||||
}),
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{path: '/', component: HomeComp}
|
||||
])
|
||||
];
|
||||
|
||||
angular.bootstrap(AppComp, routerInjectables);
|
||||
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
The <code>ngComponentRouter</code> module provides a new service, <code>$router</code>. In the configuration, we map paths
|
||||
to components. What's a component? Let's talk about that for a bit.
|
||||
|
||||
p.is-angular2.
|
||||
The <code>angular2/router</code> module provides <code>routerInjectables</code>, which is an array of all of the services
|
||||
you'll need to use the component router in your app.
|
||||
|
||||
.l-main-section
|
||||
h2#section-map-paths-to-components Map paths to components
|
||||
|
||||
//- TODO - Alex - would it make more sense to have some paragraph styles conditionalized like this??
|
||||
p.angular1.is-hidden.
|
||||
In Angular 1, a "routable component" is a template, plus a controller, plus a router. You can configure how to map
|
||||
component names to controllers and templates in the <code>$componentLoader</code> service.
|
||||
|
||||
p.
|
||||
A component's template can have "outlets," which are holes in the DOM for loading parts of your app based on the
|
||||
route configuration and it can ask the DI system for an instance of Router. A component's router tells the component what to put
|
||||
inside the outlets based on URL. The configuration maps routes to components for each outlet.
|
||||
|
||||
p Let's make a <code>home</code> component that our app can route to:
|
||||
|
||||
pre.prettyprint.lang-bash
|
||||
code.
|
||||
mkdir -p components/home
|
||||
touch components/home/home.html components/home/home.js
|
||||
|
||||
p This creates our component directory and its corresponding files: a template and a JavaScript component.
|
||||
|
||||
p Let's open <code>home.html</code> and add some content:
|
||||
|
||||
pre.prettyprint.lang-html
|
||||
code.
|
||||
<h1>Hello {{home.name}}!</h1>
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
Components use the "controller as" syntax, so if we want to access property <code>name</code> of the controller, we
|
||||
write the binding as <code>home.name</code>.
|
||||
|
||||
p Let's make a controller:
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-javascript.is-angular1.is-hidden
|
||||
code.
|
||||
angular.module('app.home', [])
|
||||
.controller('HomeController', [function () {
|
||||
this.name = 'Friend';
|
||||
}]);
|
||||
|
||||
// ANGULAR 2
|
||||
.code-box
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
@Component({
|
||||
selector: 'home-cmp'
|
||||
})
|
||||
@View({
|
||||
template: 'Hello {{name}}'
|
||||
})
|
||||
export class HomeComponent {
|
||||
name:string;
|
||||
constructor() {
|
||||
this.name = 'Friend';
|
||||
}
|
||||
}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function HomeComponent() {
|
||||
this.name = 'Friend';
|
||||
}
|
||||
|
||||
AppComponent.annotations = [
|
||||
new angular.ComponentAnnotation({
|
||||
selector: 'home-cmp'
|
||||
}),
|
||||
new angular.ViewAnnotation({
|
||||
template: 'Hello {{name}}'
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
To wire this up, we need to add a <code><script></code> tag to our <code>index.html</code>:
|
||||
pre.prettyprint.lang-html
|
||||
code.
|
||||
...
|
||||
<script src="./components/home/home.js"></script>
|
||||
|
||||
//ANGULAR 1
|
||||
p.is-angular1.is-hidden.
|
||||
And add the controller's module as a dependency to our main module in <code>app.js</code>:
|
||||
pre.prettyprint.lang-javascript.is-angular1.is-hidden
|
||||
code.
|
||||
angular.module('app', ['ngNewRouter', 'app.home'])
|
||||
.controller('AppController', ['$router', AppController]);
|
||||
// ...
|
||||
|
||||
p.
|
||||
To wire this up, we need to import the component into the rest of our app.
|
||||
// ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
import {HomeComp} from './components/home';
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
// Use your favorite module system / bundler for ES5.
|
||||
|
||||
|
||||
p If you load up the app, you should see <code>Hello Friend!</code>
|
||||
|
||||
.l-main-section
|
||||
h2#section-link-to-routes Link to routes
|
||||
|
||||
p Let's add another route and then link to it. This route will have a route parameter, <code>id</code>.
|
||||
|
||||
p In <code>app.js</code>:
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-javascript.is-hidden
|
||||
code.
|
||||
angular.module('app', ['ngNewRouter'])
|
||||
.controller('AppController', ['$router', AppController]);
|
||||
AppController.$routeConfig = [
|
||||
{ path: '/', component: 'home' },
|
||||
{ path: '/detail/:id', component: 'detail' }
|
||||
];
|
||||
function AppController ($router) {}
|
||||
|
||||
// ANGULAR 2
|
||||
.code-box
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
...
|
||||
@RouteConfig([
|
||||
{ path: '/', component: HomeComp },
|
||||
{ path: '/detail/:id', component: DetailComp }
|
||||
])
|
||||
class AppComp {}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
var AppComp = function() {};
|
||||
AppComp.annotations = [
|
||||
...
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{ path: '/', component: HomeComp}
|
||||
{ path: '/detail/:id', component: DetailComp }
|
||||
])
|
||||
];
|
||||
|
||||
angular.bootstrap(AppComp, routerInjectables);
|
||||
|
||||
|
||||
p.
|
||||
We can link to our detail component using the
|
||||
<code class="is-angular2">router-link</code><code class="is-angular1 is-hidden">ng-link</code> directive.
|
||||
Add this to <code class="is-angular2">template</code><code class="is-angular1 is-hidden">index.html</code>:
|
||||
|
||||
pre.prettyprint.lang-html.is-angular1.is-hidden.
|
||||
code.
|
||||
<body ng-app="myApp" ng-controller="AppController as app">
|
||||
<a ng-link="detail({id: 5})">link to detail</a>
|
||||
...
|
||||
|
||||
pre.prettyprint.lang-html.is-angular2
|
||||
code.
|
||||
<a ng-link="detail({id: 5})">link to detail</a>
|
||||
|
||||
p This directive will generate an <code>href</code> and update the browser URL.
|
||||
|
||||
p We should also implement our detail component. Let's make these new files:
|
||||
|
||||
pre.prettyprint.lang-bash
|
||||
code.
|
||||
mkdir components/detail
|
||||
touch components/detail/detail.html components/detail/detail.ts
|
||||
|
||||
p In <code>detail.ts</code>, we implement a controller that uses the <code>id</code> route parameter:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-javascript.is-hidden
|
||||
code.
|
||||
angular.module('app.detail', ['ngNewRouter'])
|
||||
.controller('DetailController', ['$routeParams', DetailController]);
|
||||
|
||||
function DetailController ($routeParams) {
|
||||
this.id = $routeParams.id;
|
||||
}
|
||||
|
||||
// ANGULAR 2
|
||||
.code-box
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
@Component({
|
||||
selector: 'detail-cmp'
|
||||
})
|
||||
@View({
|
||||
template: 'User ID: {{id}}'
|
||||
})
|
||||
export class DetailComp {
|
||||
id: string;
|
||||
constructor(routeParams:RouteParams) {
|
||||
this.id = routeParams.get('id');
|
||||
}
|
||||
}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function DetailComp(routeParams) {
|
||||
this.id = routeParams.get('id');
|
||||
}
|
||||
|
||||
DetailComp.annotations = [
|
||||
new angular.ComponentAnnotation({
|
||||
selector: 'detail-cmp'
|
||||
}),
|
||||
new angular.ViewAnnotation({
|
||||
template: 'User ID: {{id}}'
|
||||
})
|
||||
];
|
||||
|
||||
DetailComp.parameters = [[RouteParams]];
|
||||
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
And then we can display the <code>id</code> in our template by adding this to <code>detail.html</code>:
|
||||
|
||||
pre.prettyprint.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
<p>detail {{detail.id}}</p>
|
||||
|
||||
p.is-angular1.is-hidden.
|
||||
Finally, we'd wire up the controller by adding a script tag and making our <code>app</code> module depend on
|
||||
<code>app.detail</code>.
|
||||
|
||||
|
||||
.l-main-section
|
||||
|
||||
h2#section-configuring-the-router Configuring the Router
|
||||
|
||||
p.
|
||||
Unlike other routing systems, Component Router maps URLs to components. A router takes an array of pairings like
|
||||
this:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.lang-javascript.is-angular1.is-hidden
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/user', component: 'user' }
|
||||
];
|
||||
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="typescript")
|
||||
code.
|
||||
@Component()
|
||||
@View()
|
||||
@RouteConfig([
|
||||
{ path: '/user', component: UserComponent }
|
||||
])
|
||||
class MyComp {}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function MyComp() {};
|
||||
MyComp.annotations = [
|
||||
new angular.ComponentAnnotation({ ... }),
|
||||
new angular.ViewAnnotation({ ... }),
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{path: '/', component: UserComponent}
|
||||
])
|
||||
|
||||
|
||||
.l-sub-section
|
||||
h3#section-sibling-outlets Sibling Outlets
|
||||
|
||||
|
||||
p You can configure multiple outlets on the same path like this:
|
||||
|
||||
//ANGULAR 1
|
||||
.codebox.is-angular1.is-hidden
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/user',
|
||||
components: {
|
||||
master: 'userList',
|
||||
detail: 'user'
|
||||
} }
|
||||
];
|
||||
|
||||
pre.prettyprint.linenums.lang-html(data-name="html")
|
||||
code.
|
||||
//HTML
|
||||
<div ng-outlet="master"></div>
|
||||
<div ng-outlet="detail"></div>
|
||||
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
//TypeScript
|
||||
@Component({})
|
||||
@View({
|
||||
template:
|
||||
`<div router-outlet="master"></div>
|
||||
<div router-outlet="detail"></div>`,
|
||||
directives: [RouterOutlet, RouterLink]
|
||||
})
|
||||
@RouteConfig({
|
||||
path: '/user', components: {
|
||||
master: UserListComp,
|
||||
detail: UserComp
|
||||
}
|
||||
})
|
||||
class MyComponent {}
|
||||
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function MyComponent() {};
|
||||
MyComponent.annotations = [
|
||||
new angular.ComponentAnnotation({ ... }),
|
||||
new angular.ViewAnnotation({
|
||||
template:
|
||||
'<div router-outlet="master"></div>' +
|
||||
'<div router-outlet="detail"></div>',
|
||||
directives: [RouterOutlet]
|
||||
}),
|
||||
new angular.router.RouteConfigAnnotation([{
|
||||
path: '/user', components: {
|
||||
master: UserComponent,
|
||||
detail: UserComp
|
||||
}
|
||||
}])
|
||||
];
|
||||
|
||||
p You can link to any sibling just as you normally would:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
//HTML
|
||||
<p>These both link to the same view:</p>
|
||||
<a ng-link="userList">link to userList</a>
|
||||
<a ng-link="user">link to user component</a>
|
||||
|
||||
//ANGULAR 2
|
||||
pre.prettyprint.linenums.lang-html.is-angular2
|
||||
code.
|
||||
//HTML
|
||||
<p>These both link to the same view:</p>
|
||||
<a router-link="userList">link to userList</a>
|
||||
<a router-link="user">link to user component</a>
|
||||
|
||||
|
||||
p Or, you can explicitly link to a outlet-component pair like this:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
//HTML
|
||||
<p>These both link to the same view:</p>
|
||||
<a ng-link="master:userList">link to userList</a>
|
||||
<a ng-link="detail:user">link to user component</a>
|
||||
|
||||
//ANGULAR 2
|
||||
pre.prettyprint.linenums.lang-html.is-angular2
|
||||
code.
|
||||
//HTML
|
||||
<p>These both link to the same view:</p>
|
||||
<a router-link="master:userList">link to userList</a>
|
||||
<a router-link="detail:user">link to user component</a>
|
||||
|
||||
.l-sub-section
|
||||
h3#section-redirecting-routes Redirecting routes
|
||||
|
||||
p You can use `redirectTo` for migrating to a new URL scheme and setting up default routes.
|
||||
|
||||
p.
|
||||
For example, as specified below, when a user navigates to `/`, the URL changes to `/user` and the outlet
|
||||
at that level loads the `user` component.
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/', redirectTo: '/user' },
|
||||
{ path: '/user', component: 'user' }
|
||||
];
|
||||
function MyController() {}
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
//TypeScript
|
||||
@Component({})
|
||||
@View({
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@RouteConfig([
|
||||
{ path: '/', redirectTo: '/user' },
|
||||
{ path: '/user', component: UserComp }
|
||||
])
|
||||
class MyComp {}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function MyComponent() {};
|
||||
MyComponent.annotations = [
|
||||
new angular.ComponentAnnotation({ ... }),
|
||||
new angular.ViewAnnotation({
|
||||
directives: [RouterOutlet]
|
||||
}),
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{ path: '/user', component: UserComp }
|
||||
{ path: '/', redirectTo: '/user' },
|
||||
])
|
||||
];
|
||||
|
||||
|
||||
.l-sub-section
|
||||
h3#section-aliases Aliases
|
||||
|
||||
p.
|
||||
When linking to a route, you normally use the name of the component. You can also specify an alias to use
|
||||
instead.
|
||||
|
||||
p Consider the following route configuration:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/', component: 'user' }
|
||||
];
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
//TypeScript
|
||||
@Component({
|
||||
selector: 'my-comp'
|
||||
})
|
||||
@View({
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@RouteConfig([
|
||||
{ path: '/', component: UserComp }
|
||||
])
|
||||
class MyComp {}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
function MyComp() {};
|
||||
MyComp.annotations = [
|
||||
new angular.ComponentAnnotation({ ... }),
|
||||
new angular.ViewAnnotation({
|
||||
directives: [RouterOutlet]
|
||||
}),
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{ path: '/', component: UserComp }
|
||||
])
|
||||
];
|
||||
|
||||
|
||||
p We can link to the route in our template with the name of the component:
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
//HTML
|
||||
<a ng-link="user">link to user component</a>
|
||||
|
||||
//ANGULAR 2
|
||||
pre.prettyprint.linenums.lang-html
|
||||
code.
|
||||
//HTML
|
||||
<a router-link="user">link to user component</a>
|
||||
|
||||
p Or, we can define an alias <code>myUser</code> like this:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/', component: 'user', as: 'myUser' }
|
||||
];
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
//TypeScript
|
||||
@Component()
|
||||
@View()
|
||||
@RouteConfig([
|
||||
{ path: '/', component: UserComp, as: 'myUser' }
|
||||
])
|
||||
class MyComp {}
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
//TODO: Need Angular 2 ES5 Example here
|
||||
|
||||
|
||||
p And refer instead to the alias for the component in our template, with the same end-result:
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
|
||||
code.
|
||||
//HTML
|
||||
<a ng-link="myUser">link to user component</a>
|
||||
|
||||
//ANGULAR 2
|
||||
pre.prettyprint.linenums.lang-html
|
||||
code.
|
||||
//HTML
|
||||
<a router-link="myUser">link to user component</a>
|
||||
|
||||
|
||||
p.
|
||||
This is especially useful when you have sibling components, but want to refer to an entire level of routing in
|
||||
your controller. For example:
|
||||
|
||||
//ANGULAR 1
|
||||
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
|
||||
code.
|
||||
//ES5
|
||||
MyController.$routeConfig = [
|
||||
{ path: '/',
|
||||
components: {
|
||||
master: 'userList',
|
||||
detail: 'user'
|
||||
},
|
||||
as: 'myUser'
|
||||
}
|
||||
];
|
||||
|
||||
//ANGULAR 2
|
||||
.code-box.is-angular2
|
||||
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
|
||||
code.
|
||||
//TypeScript
|
||||
@RouteConfig([
|
||||
{ path: '/', components:
|
||||
{ master: UserListComp, detail: UserComp },
|
||||
as: 'myUser' }
|
||||
])
|
||||
pre.prettyprint.linenums.lang-javascript(data-name="es5")
|
||||
code.
|
||||
new angular.router.RouteConfigAnnotation([
|
||||
{ path: '/', components:
|
||||
{ master: UserListComp, detail: UserComp },
|
||||
as: 'myUser' }
|
||||
])
|
||||
|
||||
|
||||
//- TODO(btford): expand on this.
|
||||
.l-sub-section
|
||||
h3#dynamic-configuration Dynamic Configuration
|
||||
|
||||
p.is-angular2.
|
||||
You can configure dynamic routing by asking the DI system for a <code>Router</code>.
|
||||
|
||||
p.is-angular1.is-hidden
|
||||
You can configure dynamic routing by making a request for <code>$router</code>.
|
|
@ -784,15 +784,15 @@ figure.image-display
|
|||
Here’s an example:
|
||||
+makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".")
|
||||
:marked
|
||||
We bind the parent `NgSwitch` element to an expression returning a “switch value”. The value is a string in this example but it can be a value of any type.
|
||||
We bind the parent `NgSwitch` directive to an expression returning a “switch value”. The value is a string in this example but it can be a value of any type.
|
||||
|
||||
The parent `NgSwitch` element controls a set of child`<template>` elements. Each `<template>` wraps a candidate subtree. A template is either pegged to a “match value” expression or marked as the default template.
|
||||
The parent `NgSwitch` directive controls a set of child`<template>` elements. Each `<template>` wraps a candidate subtree. A template is either pegged to a “match value” expression or marked as the default template.
|
||||
|
||||
**At any particular moment, only one of these templates will be rendered**
|
||||
|
||||
If the template’s “match value” equals the “switch value”, Angular adds the template’s sub-tree to the DOM. If no template is a match and there is a default template, Angular adds the default template’s sub-tree to the DOM. Angular removes and destroys the sub-tree’s of all other templates.
|
||||
|
||||
There are three related directives at work here.
|
||||
There are three collaborating directives at work here.
|
||||
1. `ngSwitch` - bound to an expression that returns the switch value.
|
||||
1. `ngSwitchWhen` - bound to an expression returning a match value.
|
||||
1. `ngSwitchDefault` - a marker attribute on the default template.
|
||||
|
|
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 367 KiB |
After Width: | Height: | Size: 32 KiB |