docs(tutorial): add part 5-routing
This commit is contained in:
parent
7d5a5fdf84
commit
1365d82527
|
@ -79,10 +79,10 @@ interface Hero {
|
|||
// #enddocregion styles-1
|
||||
})
|
||||
export class AppComponent {
|
||||
public title = 'Tour of Heroes';
|
||||
public heroes = HEROES;
|
||||
title = 'Tour of Heroes';
|
||||
heroes = HEROES;
|
||||
// #docregion selected-hero-1
|
||||
public selectedHero: Hero;
|
||||
selectedHero: Hero;
|
||||
// #enddocregion selected-hero-1
|
||||
|
||||
// #docregion on-select-1
|
||||
|
|
|
@ -76,9 +76,9 @@ import {HeroDetailComponent} from './hero-detail.component';
|
|||
// #enddocregion directives
|
||||
})
|
||||
export class AppComponent {
|
||||
public title = 'Tour of Heroes';
|
||||
public heroes = HEROES;
|
||||
public selectedHero: Hero;
|
||||
title = 'Tour of Heroes';
|
||||
heroes = HEROES;
|
||||
selectedHero: Hero;
|
||||
|
||||
onSelect(hero: Hero) { this.selectedHero = hero; }
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import {Hero} from './hero';
|
|||
export class HeroDetailComponent {
|
||||
// #enddocregion v1
|
||||
// #docregion hero
|
||||
public hero: Hero;
|
||||
hero: Hero;
|
||||
// #enddocregion hero
|
||||
// #docregion v1
|
||||
}
|
||||
|
|
|
@ -27,11 +27,11 @@ import {HeroService} from './hero.service.1';
|
|||
// #docregion on-init
|
||||
export class AppComponent implements OnInit {
|
||||
// #enddocregion on-init
|
||||
public title = 'Tour of Heroes';
|
||||
title = 'Tour of Heroes';
|
||||
// #docregion heroes-prop
|
||||
public heroes: Hero[];
|
||||
heroes: Hero[];
|
||||
// #enddocregion heroes-prop
|
||||
public selectedHero: Hero;
|
||||
selectedHero: Hero;
|
||||
|
||||
// #docregion new-service
|
||||
heroService = new HeroService(); // don't do this
|
||||
|
@ -61,3 +61,4 @@ export class AppComponent implements OnInit {
|
|||
// #docregion on-init
|
||||
}
|
||||
// #enddocregion on-init
|
||||
// #enddocregion
|
|
@ -9,6 +9,7 @@ import {HeroService} from './hero.service';
|
|||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template:`
|
||||
<h1>{{title}}</h1>
|
||||
<h2>My Heroes</h2>
|
||||
|
@ -21,6 +22,7 @@ import {HeroService} from './hero.service';
|
|||
</ul>
|
||||
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styles:[`
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
|
@ -73,9 +75,9 @@ import {HeroService} from './hero.service';
|
|||
providers: [HeroService]
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
public title = 'Tour of Heroes';
|
||||
public heroes: Hero[];
|
||||
public selectedHero: Hero;
|
||||
title = 'Tour of Heroes';
|
||||
heroes: Hero[];
|
||||
selectedHero: Hero;
|
||||
|
||||
constructor(private _heroService: HeroService) { }
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {Hero} from './hero';
|
||||
|
||||
|
@ -5,8 +6,10 @@ import {Hero} from './hero';
|
|||
selector: 'my-hero-detail',
|
||||
template: `
|
||||
<div *ngIf="hero">
|
||||
<h2>{{hero.name}} details!</h2>
|
||||
<div><label>id: </label>{{hero.id}}</div>
|
||||
<h2>{{hero.name}} details</h2>
|
||||
<div>
|
||||
<label>id: </label>{{hero.id}}
|
||||
</div>
|
||||
<div>
|
||||
<label>name: </label>
|
||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
|
@ -16,5 +19,5 @@ import {Hero} from './hero';
|
|||
inputs: ['hero']
|
||||
})
|
||||
export class HeroDetailComponent {
|
||||
public hero: Hero;
|
||||
hero: Hero;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
describe('Tutorial', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
|
||||
function getPageStruct() {
|
||||
hrefEles = element.all(by.css('my-app a'));
|
||||
|
||||
return {
|
||||
hrefs: hrefEles,
|
||||
myDashboardHref: hrefEles.get(0),
|
||||
myDashboardParent: element(by.css('my-app my-dashboard')),
|
||||
topHeroes: element.all(by.css('my-app my-dashboard .module.hero')),
|
||||
|
||||
myHeroesHref: hrefEles.get(1),
|
||||
myHeroesParent: element(by.css('my-app my-heroes')),
|
||||
allHeroes: element.all(by.css('my-app my-heroes li')),
|
||||
|
||||
heroDetail: element(by.css('my-app my-hero-detail'))
|
||||
}
|
||||
}
|
||||
|
||||
it('should be able to see the start screen', function () {
|
||||
var page = getPageStruct();
|
||||
expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
|
||||
expect(page.myDashboardHref.getText()).toEqual("Dashboard");
|
||||
expect(page.myHeroesHref.getText()).toEqual("Heroes");
|
||||
});
|
||||
|
||||
it('should be able to see dashboard choices', function () {
|
||||
var page = getPageStruct();
|
||||
expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices");
|
||||
});
|
||||
|
||||
it('should be able to toggle the views', function () {
|
||||
var page = getPageStruct();
|
||||
|
||||
expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes');
|
||||
page.myHeroesHref.click().then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'should no longer see dashboard element');
|
||||
expect(page.allHeroes.count()).toBeGreaterThan(4, "should be more than 4 heroes shown");
|
||||
return page.myDashboardHref.click();
|
||||
}).then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(true, 'should once again see the dashboard element');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should be able to edit details from "Dashboard" view', function () {
|
||||
var page = getPageStruct();
|
||||
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available');
|
||||
var heroEle = page.topHeroes.get(3);
|
||||
var heroDescrEle = heroEle.element(by.css('h4'));
|
||||
var heroDescr;
|
||||
return heroDescrEle.getText().then(function(text) {
|
||||
heroDescr = text;
|
||||
return heroEle.click();
|
||||
}).then(function() {
|
||||
return editDetails(page, heroDescr, '-foo');
|
||||
}).then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be back');
|
||||
expect(heroDescrEle.getText()).toEqual(heroDescr + '-foo');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to edit details from "Heroes" view', function () {
|
||||
var page = getPageStruct();
|
||||
expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present');
|
||||
var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details'));
|
||||
var heroEle, heroDescr;
|
||||
page.myHeroesHref.click().then(function() {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
|
||||
expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present');
|
||||
expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should not yet be present');
|
||||
heroEle = page.allHeroes.get(2);
|
||||
return heroEle.getText();
|
||||
}).then(function(text) {
|
||||
// remove leading 'id' from the element
|
||||
heroDescr = text.substr(text.indexOf(' ')+1);
|
||||
return heroEle.click();
|
||||
}).then(function() {
|
||||
expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible');
|
||||
return viewDetailsButtonEle.click();
|
||||
}).then(function() {
|
||||
return editDetails(page, heroDescr, '-bar');
|
||||
}).then(function() {
|
||||
expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be back');
|
||||
expect(heroEle.getText()).toContain(heroDescr + '-bar');
|
||||
expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should again NOT be present');
|
||||
});
|
||||
});
|
||||
|
||||
function editDetails(page, origValue, textToAdd) {
|
||||
expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present');
|
||||
expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present');
|
||||
expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details');
|
||||
var inputEle = page.heroDetail.element(by.css('input'));
|
||||
expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box');
|
||||
var backButtonEle = page.heroDetail.element(by.css('button'));
|
||||
expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button');
|
||||
var detailTextEle = page.heroDetail.element(by.css('div h2'));
|
||||
expect(detailTextEle.getText()).toContain(origValue);
|
||||
return sendKeys(inputEle, textToAdd).then(function () {
|
||||
expect(detailTextEle.getText()).toContain(origValue + textToAdd);
|
||||
return backButtonEle.click();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
|
@ -1,111 +0,0 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/angular2';
|
||||
|
||||
@Component({selector: 'my-app'})
|
||||
export class AppComponent { }
|
||||
// #enddocregion
|
||||
|
||||
// #docregion initialize-routes-property
|
||||
export class AppComponent {
|
||||
public title = 'Tour of Heroes';
|
||||
public routes = Routes;
|
||||
}
|
||||
// #enddocregion initialize-routes-property
|
||||
|
||||
// #docregion oninit
|
||||
onInit() {
|
||||
this.heroes = this.getHeroes();
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
this.heroes = [];
|
||||
|
||||
this._heroService.getHeroes()
|
||||
.then((heroes: Hero[]) => this.heroes = heroes);
|
||||
|
||||
return this.heroes;
|
||||
}
|
||||
// #enddocregion oninit
|
||||
|
||||
// #docregion styles
|
||||
styles: [`
|
||||
.router-link {padding: 5px;text-decoration: none;}
|
||||
.router-link:visited, .router-link:link {color: #444;}
|
||||
.router-link:hover {color: white; background-color: #1171a3; text-decoration: none;}
|
||||
.router-link.router-link-active {color: white; background-color: #52b9e9; text-decoration: none;}
|
||||
`],
|
||||
// #enddocregion styles
|
||||
|
||||
// #docregion import-params
|
||||
import {RouteParams} from 'angular2/router';
|
||||
// #enddocregion import-params
|
||||
// #docregion inject-routeparams
|
||||
constructor(private _routeParams: RouteParams) {}
|
||||
// #enddocregion inject-routeparams
|
||||
// #docregion access-params
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
|
||||
onInit() { }
|
||||
// #enddocregion access-params
|
||||
// #docregion import-onit
|
||||
import {Component, FORM_DIRECTIVES, OnInit} from 'angular2/angular2';
|
||||
// #enddocregion import-onit
|
||||
// #docregion onit-id-param
|
||||
onInit() { let id = +this._routeParams.get('id'); // TODO: get the hero using it’s id }
|
||||
// #enddocregion onit-id-param
|
||||
|
||||
// #docregion onit-hero-id
|
||||
onInit() {
|
||||
if (!this.hero) {
|
||||
let id = +this._routeParams.get('id');
|
||||
// TODO: get the hero using it’s id
|
||||
}
|
||||
}
|
||||
// #docregion onit-hero-id
|
||||
|
||||
// #docregion inject-hero-service
|
||||
constructor(
|
||||
private _heroService: HeroService,
|
||||
private _routeParams: RouteParams) {
|
||||
}
|
||||
// #docregion inject-hero-service
|
||||
|
||||
// #docregion onit-hero-method
|
||||
onInit() {
|
||||
if (!this.hero) {
|
||||
let id = +this._routeParams.get('id');
|
||||
this._heroService.getHero(id).then((hero: Hero) => this.hero = hero);
|
||||
}
|
||||
}
|
||||
// #docregion onit-hero-method
|
||||
|
||||
// #docregion select-hero
|
||||
import {Router} from 'angular2/router';
|
||||
import {Routes} from './route.config';
|
||||
|
||||
constructor(private _heroService: HeroService, private _router: Router) { }
|
||||
|
||||
gotoDetail() {
|
||||
this._router.navigate([`/${Routes.detail.as}`, { id: this.selectedHero.id }]);
|
||||
}
|
||||
// #enddocregion select-hero
|
||||
|
||||
// #docregion reference-heroes-component
|
||||
@Component({
|
||||
selector: 'my-heroes',
|
||||
templateUrl: 'app/heroes.component.html',
|
||||
styleUrls: ['app/heroes.component.css'],
|
||||
directives: [FORM_DIRECTIVES, HeroDetailComponent]
|
||||
})
|
||||
export class HeroesComponent {
|
||||
// #docregion reference-heroes-component
|
||||
|
||||
// #docregion reference-hero-detail-component
|
||||
@Component({
|
||||
selector: 'my-hero-detail',
|
||||
templateUrl: 'app/hero-detail.component.html',
|
||||
directives: [FORM_DIRECTIVES],
|
||||
inputs: ['hero']
|
||||
})
|
||||
export class HeroDetailComponent {
|
||||
// #enddocregion reference-hero-detail-component
|
|
@ -1,8 +0,0 @@
|
|||
// #docregion get-hero-method
|
||||
getHero(id: number) {
|
||||
return Promise.resolve(HEROES)
|
||||
.then((heroes: Hero[]) => { return heroes.filter((h) => {
|
||||
return h.id === id;
|
||||
})[0]});
|
||||
}
|
||||
// #docregion get-hero-method
|
|
@ -1,186 +0,0 @@
|
|||
<!-- #docregion head -->
|
||||
<head>
|
||||
<base href="/src/" />
|
||||
<!-- #docregion stylesheet -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- #enddocregion stylesheet -->
|
||||
<script src="../node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||
<script src="../node_modules/angular2/bundles/router.dev.js"></script>
|
||||
</head>
|
||||
<!-- #enddocregion head -->
|
||||
<!-- #docregion bootstrap -->
|
||||
System.import('app/bootstrap');
|
||||
<!-- #enddocregion bootstrap -->
|
||||
<!-- #docregion title -->
|
||||
import {Component} from 'angular2/angular2';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
`
|
||||
})
|
||||
export class AppComponent {
|
||||
public title = 'Tour of Heroes';
|
||||
}
|
||||
<!-- #enddocregion title -->
|
||||
|
||||
<!-- #docregion routes-title -->
|
||||
import {Component} from 'angular2/angular2';
|
||||
import {RouteConfig} from 'angular2/router';
|
||||
<!-- #docregion import-app-routes -->
|
||||
import {APP_ROUTES} from './route.config';
|
||||
<!-- #enddocregion import-app-routes -->
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
`
|
||||
})
|
||||
@RouteConfig(APP_ROUTES)
|
||||
export class AppComponent {
|
||||
public title = 'Tour of Heroes';
|
||||
}
|
||||
<!-- #enddocregion routes-title -->
|
||||
|
||||
<!-- #docregion router-outlet -->
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<router-outlet></router-outlet>
|
||||
`
|
||||
<!-- #enddocregion router-outlet -->
|
||||
|
||||
<!-- #docregion simple-dashboard-component -->
|
||||
import {Component} from 'angular2/angular2';
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
template: `
|
||||
<h1>Dashboard Goes Here</h1>
|
||||
`
|
||||
})
|
||||
export class DashboardComponent {}
|
||||
<!-- #enddocregion simple-dashboard-component -->
|
||||
|
||||
<!-- #docregion router-link -->
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<a [router-link]="/Dashboard">Dashboard</a>
|
||||
<a [router-link]="/Heroes">Heroes</a>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
<!-- #enddocregion router-link -->
|
||||
|
||||
<!-- #docregion ng-for -->
|
||||
template: `
|
||||
<h3>Top Heroes</h3>
|
||||
<div>
|
||||
<div *ng-for="#hero of heroes">
|
||||
<div>
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
<!-- #enddocregion ng-for -->
|
||||
|
||||
<!-- #docregion css -->
|
||||
[class*='col-'] { float: left; }
|
||||
*, *:after, *:before {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h3 { text-align: center; margin-bottom: 0; }
|
||||
[class*='col-'] { padding-right: 20px; padding-bottom: 20px;}
|
||||
[class*='col-']:last-of-type { padding-right: 0; }
|
||||
.grid { margin: 0 10em; }
|
||||
.col-1-4 { width: 25%; }
|
||||
.module {
|
||||
padding: 20px; text-align: center;
|
||||
color: #eee; max-height: 120px; min-width: 120px;
|
||||
background-color: #1171a3;
|
||||
}
|
||||
.module:hover { background-color: #52b9e9; cursor: pointer; }
|
||||
.grid-pad { padding: 20px 0 20px 20px; }
|
||||
.grid-pad > [class*='col-']:last-of-type { padding-right: 20px; }
|
||||
@media (max-width: 600px) {
|
||||
.module { font-size: 10px; max-height: 75px; }
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.grid { margin: 0; }
|
||||
.module { min-width: 60px; }
|
||||
}
|
||||
<!-- #enddocregion css -->
|
||||
|
||||
<!-- #docregion template-styled -->
|
||||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<div *ng-for="#hero of heroes | slice:0:4" class="col-1-4">
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion template-styled -->
|
||||
|
||||
<!-- #docregion styled-nav-links -->
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<a [router-link]="['/' + routes.dashboard.as]"
|
||||
class="router-link">Dashboard</a>
|
||||
<a [router-link]="['/' + routes.heroes.as]"
|
||||
class="router-link">Heroes</a>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
styles: [`
|
||||
.router-link {padding: 5px;text-decoration: none;}
|
||||
.router-link:visited, .router-link:link {color: #444;}
|
||||
.router-link:hover {color: white; background-color: #1171a3;
|
||||
text-decoration: none;}
|
||||
.router-link.router-link-active {color: white;
|
||||
background-color: #52b9e9; text-decoration: none;}
|
||||
`],
|
||||
directives: [ROUTER_DIRECTIVES]
|
||||
})
|
||||
<!-- #docregion styled-nav-links -->
|
||||
|
||||
<!-- #docregion select-hero-click-event -->
|
||||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<div *ng-for="#hero of heroes | slice:0:4" class="col-1-4"
|
||||
(click)="gotoDetail(hero)">
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #docregion select-hero-click-event -->
|
||||
|
||||
<!-- #docregion display-hero-name -->
|
||||
<div>
|
||||
<h2>My Heroes</h2>
|
||||
<ul class="heroes">
|
||||
<li *ng-for="#hero of heroes"
|
||||
[ng-class]="getSelectedClass(hero)"
|
||||
(click)="onSelect(hero)">
|
||||
<span class="badge">{{hero.id}}</span> {{hero.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div *ng-if="selectedHero">
|
||||
<h2>{{selectedHero.name | uppercase}} is my hero</h2>
|
||||
<button (click)="gotoDetail()">View Details</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion display-hero-name -->
|
||||
|
||||
<!-- #docregion basic-styles -->
|
||||
h2 { 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}
|
||||
<!-- #enddocregion basic-styles -->
|
|
@ -1,13 +0,0 @@
|
|||
// #docregion
|
||||
import {bootstrap} from 'angular2/angular2';
|
||||
import {AppComponent} from './app.component';
|
||||
// #docregion import-hero-service
|
||||
import {HeroService} from './hero.service';
|
||||
// #enddocregion import-hero-service
|
||||
bootstrap(AppComponent, [HeroService]);
|
||||
// #enddocregion
|
||||
|
||||
// #docregion import-router
|
||||
import {Router} from 'angular2/router';
|
||||
import {Routes} from './route.config';
|
||||
// #enddocregion import-router
|
|
@ -1,50 +0,0 @@
|
|||
// #docregion first-route
|
||||
import {HeroesComponent} from './heroes.component';
|
||||
|
||||
export var Routes = {
|
||||
heroes: {
|
||||
path: '/,
|
||||
as: 'Heroes',
|
||||
component: HeroesComponent
|
||||
}
|
||||
};
|
||||
|
||||
export const APP_ROUTES = Object.keys(Routes).map(r => Routes[r]);
|
||||
// #enddocregion first-route
|
||||
|
||||
// #docregion dashboard-route
|
||||
import {HeroesComponent} from './heroes.component';
|
||||
import {DashboardComponent} from './dashboard.component';
|
||||
|
||||
export var Routes = {
|
||||
dashboard: {
|
||||
path: '/',
|
||||
as: 'Dashboard',
|
||||
component: DashboardComponent
|
||||
},
|
||||
heroes: {
|
||||
path: '/heroes',
|
||||
as: 'Heroes',
|
||||
component: HeroesComponent
|
||||
}
|
||||
};
|
||||
|
||||
export const APP_ROUTES = Object.keys(Routes).map(r => Routes[r]);
|
||||
// #enddocregion dashboard-route
|
||||
|
||||
// #docregion route-parameter-import
|
||||
import {HeroDetailComponent} from './hero-detail.component';
|
||||
// #enddocregion route-parameter-import
|
||||
// #docregion route-parameter-detail
|
||||
detail: {
|
||||
path: '/detail/:id',
|
||||
as: 'Detail',
|
||||
component: HeroDetailComponent
|
||||
}
|
||||
// #docregion route-parameter-detail
|
||||
|
||||
// #docregion router-navigate-method
|
||||
gotoDetail(hero: Hero) {
|
||||
this._router.navigate([`/${Routes.detail.as}`, { id: hero.id }]);
|
||||
}
|
||||
// #docregion router-navigate-method
|
|
@ -0,0 +1,54 @@
|
|||
System.register(['angular2/core', './hero.service', './heroes.component', 'angular2/router'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, hero_service_1, heroes_component_1, router_1;
|
||||
var AppComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
},
|
||||
function (heroes_component_1_1) {
|
||||
heroes_component_1 = heroes_component_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
// #docregion
|
||||
AppComponent = (function () {
|
||||
function AppComponent() {
|
||||
this.title = 'Tour of Heroes';
|
||||
}
|
||||
AppComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-app',
|
||||
template: "\n <h1>{{title}}</h1>\n <my-heroes></my-heroes>\n ",
|
||||
directives: [heroes_component_1.HeroesComponent],
|
||||
providers: [
|
||||
// #enddocregion
|
||||
router_1.ROUTER_PROVIDERS,
|
||||
// #docregion
|
||||
hero_service_1.HeroService
|
||||
]
|
||||
}),
|
||||
__metadata('design:paramtypes', [])
|
||||
], AppComponent);
|
||||
return AppComponent;
|
||||
})();
|
||||
exports_1("AppComponent", AppComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=app.component.1.js.map
|
|
@ -0,0 +1,30 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from 'angular2/core';
|
||||
import { HeroService } from './hero.service';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
// #enddocregion
|
||||
|
||||
// For testing only
|
||||
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
|
||||
|
||||
// #docregion
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<my-heroes></my-heroes>
|
||||
`,
|
||||
directives: [HeroesComponent],
|
||||
providers: [
|
||||
// #enddocregion
|
||||
ROUTER_PROVIDERS,
|
||||
// #docregion
|
||||
HeroService
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Tour of Heroes';
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,61 @@
|
|||
System.register(['angular2/core', 'angular2/router', './hero.service', './heroes.component'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, router_1, hero_service_1, heroes_component_1;
|
||||
var AppComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
},
|
||||
function (heroes_component_1_1) {
|
||||
heroes_component_1 = heroes_component_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
AppComponent = (function () {
|
||||
function AppComponent() {
|
||||
this.title = 'Tour of Heroes';
|
||||
}
|
||||
AppComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: "\n <h1>{{title}}</h1>\n <a [routerLink]=\"['Heroes']\">Heroes</a>\n <router-outlet></router-outlet>\n ",
|
||||
// #enddocregion template
|
||||
// #docregion directives-and-providers
|
||||
directives: [router_1.ROUTER_DIRECTIVES],
|
||||
providers: [
|
||||
router_1.ROUTER_PROVIDERS,
|
||||
hero_service_1.HeroService
|
||||
]
|
||||
}),
|
||||
router_1.RouteConfig([
|
||||
{
|
||||
path: '/heroes',
|
||||
name: 'Heroes',
|
||||
component: heroes_component_1.HeroesComponent
|
||||
}
|
||||
]),
|
||||
__metadata('design:paramtypes', [])
|
||||
], AppComponent);
|
||||
return AppComponent;
|
||||
})();
|
||||
exports_1("AppComponent", AppComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=app.component.2.js.map
|
|
@ -0,0 +1,40 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from 'angular2/core';
|
||||
// #docregion import-router
|
||||
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
|
||||
// #enddocregion import-router
|
||||
|
||||
import { HeroService } from './hero.service';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<a [routerLink]="['Heroes']">Heroes</a>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
// #enddocregion template
|
||||
// #docregion directives-and-providers
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
providers: [
|
||||
ROUTER_PROVIDERS,
|
||||
HeroService
|
||||
]
|
||||
// #enddocregion directives-and-providers
|
||||
})
|
||||
// #docregion route-config
|
||||
@RouteConfig([
|
||||
{
|
||||
path: '/heroes',
|
||||
name: 'Heroes',
|
||||
component: HeroesComponent
|
||||
}
|
||||
])
|
||||
// #enddocregion route-config
|
||||
export class AppComponent {
|
||||
title = 'Tour of Heroes';
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,31 @@
|
|||
/* #docplaster */
|
||||
/* #docregion css */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
nav a {
|
||||
padding: 5px 10px;
|
||||
text-decoration: none;
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
nav a:visited, a:link {
|
||||
color: #607D8B;
|
||||
}
|
||||
nav a:hover {
|
||||
color: #039be5;
|
||||
background-color: #CFD8DC;
|
||||
}
|
||||
nav a.router-link-active {
|
||||
color: #039be5;
|
||||
}
|
||||
/* #enddocregion css */
|
|
@ -0,0 +1,85 @@
|
|||
System.register(['angular2/core', 'angular2/router', './hero.service', './dashboard.component', './heroes.component', './hero-detail.component'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, router_1, hero_service_1, dashboard_component_1, heroes_component_1, hero_detail_component_1;
|
||||
var AppComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
},
|
||||
function (dashboard_component_1_1) {
|
||||
dashboard_component_1 = dashboard_component_1_1;
|
||||
},
|
||||
function (heroes_component_1_1) {
|
||||
heroes_component_1 = heroes_component_1_1;
|
||||
},
|
||||
function (hero_detail_component_1_1) {
|
||||
hero_detail_component_1 = hero_detail_component_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
// #enddocregion hero-detail-import
|
||||
AppComponent = (function () {
|
||||
function AppComponent() {
|
||||
this.title = 'Tour of Heroes';
|
||||
}
|
||||
AppComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: "\n <h1>{{title}}</h1>\n <nav>\n <a [routerLink]=\"['Dashboard']\">Dashboard</a>\n <a [routerLink]=\"['Heroes']\">Heroes</a>\n </nav>\n <router-outlet></router-outlet>\n ",
|
||||
// #enddocregion template
|
||||
// #docregion style-urls
|
||||
styleUrls: ['app/app.component.css'],
|
||||
// #enddocregion style-urls
|
||||
directives: [router_1.ROUTER_DIRECTIVES],
|
||||
providers: [
|
||||
router_1.ROUTER_PROVIDERS,
|
||||
hero_service_1.HeroService
|
||||
]
|
||||
}),
|
||||
router_1.RouteConfig([
|
||||
// #docregion dashboard-route
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: dashboard_component_1.DashboardComponent,
|
||||
useAsDefault: true
|
||||
},
|
||||
// #enddocregion dashboard-route
|
||||
// #docregion hero-detail-route
|
||||
{
|
||||
path: '/detail/:id',
|
||||
name: 'HeroDetail',
|
||||
component: hero_detail_component_1.HeroDetailComponent
|
||||
},
|
||||
// #enddocregion hero-detail-route
|
||||
{
|
||||
path: '/heroes',
|
||||
name: 'Heroes',
|
||||
component: heroes_component_1.HeroesComponent
|
||||
}
|
||||
]),
|
||||
__metadata('design:paramtypes', [])
|
||||
], AppComponent);
|
||||
return AppComponent;
|
||||
})();
|
||||
exports_1("AppComponent", AppComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=app.component.js.map
|
|
@ -0,0 +1,59 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from 'angular2/core';
|
||||
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
|
||||
|
||||
import { HeroService } from './hero.service';
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
// #docregion hero-detail-import
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
// #enddocregion hero-detail-import
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
// #docregion template
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<nav>
|
||||
<a [routerLink]="['Dashboard']">Dashboard</a>
|
||||
<a [routerLink]="['Heroes']">Heroes</a>
|
||||
</nav>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
// #enddocregion template
|
||||
// #docregion style-urls
|
||||
styleUrls: ['app/app.component.css'],
|
||||
// #enddocregion style-urls
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
providers: [
|
||||
ROUTER_PROVIDERS,
|
||||
HeroService
|
||||
]
|
||||
})
|
||||
@RouteConfig([
|
||||
// #docregion dashboard-route
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: DashboardComponent,
|
||||
useAsDefault: true
|
||||
},
|
||||
// #enddocregion dashboard-route
|
||||
// #docregion hero-detail-route
|
||||
{
|
||||
path: '/detail/:id',
|
||||
name: 'HeroDetail',
|
||||
component: HeroDetailComponent
|
||||
},
|
||||
// #enddocregion hero-detail-route
|
||||
{
|
||||
path: '/heroes',
|
||||
name: 'Heroes',
|
||||
component: HeroesComponent
|
||||
}
|
||||
])
|
||||
export class AppComponent {
|
||||
title = 'Tour of Heroes';
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,35 @@
|
|||
System.register(['angular2/core'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1;
|
||||
var DashboardComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
DashboardComponent = (function () {
|
||||
function DashboardComponent() {
|
||||
}
|
||||
DashboardComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-dashboard',
|
||||
template: '<h3>My Dashboard</h3>'
|
||||
}),
|
||||
__metadata('design:paramtypes', [])
|
||||
], DashboardComponent);
|
||||
return DashboardComponent;
|
||||
})();
|
||||
exports_1("DashboardComponent", DashboardComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=dashboard.component.1.js.map
|
|
@ -0,0 +1,8 @@
|
|||
// #docregion
|
||||
import { Component } from 'angular2/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
template: '<h3>My Dashboard</h3>'
|
||||
})
|
||||
export class DashboardComponent { }
|
|
@ -0,0 +1,48 @@
|
|||
System.register(['angular2/core', './hero.service'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, hero_service_1;
|
||||
var DashboardComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
// #enddocregion imports
|
||||
DashboardComponent = (function () {
|
||||
function DashboardComponent(_heroService) {
|
||||
this._heroService = _heroService;
|
||||
this.heroes = [];
|
||||
}
|
||||
DashboardComponent.prototype.ngOnInit = function () {
|
||||
var _this = this;
|
||||
this._heroService.getHeroes()
|
||||
.then(function (heroes) { return _this.heroes = heroes.slice(1, 5); });
|
||||
};
|
||||
DashboardComponent.prototype.gotoDetail = function () { };
|
||||
DashboardComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-dashboard',
|
||||
templateUrl: 'app/dashboard.component.html'
|
||||
}),
|
||||
__metadata('design:paramtypes', [hero_service_1.HeroService])
|
||||
], DashboardComponent);
|
||||
return DashboardComponent;
|
||||
})();
|
||||
exports_1("DashboardComponent", DashboardComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion component
|
||||
//# sourceMappingURL=dashboard.component.2.js.map
|
|
@ -0,0 +1,27 @@
|
|||
// #docplaster
|
||||
// #docregion imports
|
||||
import { Component, OnInit } from 'angular2/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
templateUrl: 'app/dashboard.component.html'
|
||||
})
|
||||
// #docregion component
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
heroes: Hero[] = [];
|
||||
|
||||
constructor(private _heroService: HeroService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this._heroService.getHeroes()
|
||||
.then(heroes => this.heroes = heroes.slice(1,5));
|
||||
}
|
||||
|
||||
gotoDetail(){ /* not implemented yet */}
|
||||
}
|
||||
// #enddocregion component
|
|
@ -0,0 +1,63 @@
|
|||
/* #docplaster */
|
||||
/* #docregion */
|
||||
[class*='col-'] {
|
||||
float: left;
|
||||
}
|
||||
*, *:after, *:before {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h3 {
|
||||
text-align: center; margin-bottom: 0;
|
||||
}
|
||||
[class*='col-'] {
|
||||
padding-right: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
[class*='col-']:last-of-type {
|
||||
padding-right: 0;
|
||||
}
|
||||
.grid {
|
||||
margin: 0 0em;
|
||||
}
|
||||
.col-1-4 {
|
||||
width: 25%;
|
||||
}
|
||||
.module {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #eee;
|
||||
max-height: 120px;
|
||||
min-width: 120px;
|
||||
background-color: #607D8B;
|
||||
border-radius: 2px;
|
||||
}
|
||||
h4 {
|
||||
position: relative;
|
||||
}
|
||||
.module:hover {
|
||||
background-color: #EEE;
|
||||
cursor: pointer;
|
||||
color: #607d8b;
|
||||
}
|
||||
.grid-pad {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.grid-pad > [class*='col-']:last-of-type {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.module {
|
||||
font-size: 10px;
|
||||
max-height: 75px; }
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.grid {
|
||||
margin: 0;
|
||||
}
|
||||
.module {
|
||||
min-width: 60px;
|
||||
}
|
||||
}
|
||||
/* #enddocregion */
|
|
@ -0,0 +1,11 @@
|
|||
<!-- #docregion -->
|
||||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<!-- #docregion click -->
|
||||
<div *ngFor="#hero of heroes" (click)="gotoDetail(hero)" class="col-1-4" >
|
||||
<!-- #enddocregion click -->
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,59 @@
|
|||
System.register(['angular2/core', 'angular2/router', './hero.service'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, router_1, hero_service_1;
|
||||
var DashboardComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
DashboardComponent = (function () {
|
||||
// #docregion ctor
|
||||
function DashboardComponent(_router, _heroService) {
|
||||
this._router = _router;
|
||||
this._heroService = _heroService;
|
||||
this.heroes = [];
|
||||
}
|
||||
// #enddocregion ctor
|
||||
DashboardComponent.prototype.ngOnInit = function () {
|
||||
var _this = this;
|
||||
this._heroService.getHeroes()
|
||||
.then(function (heroes) { return _this.heroes = heroes.slice(1, 5); });
|
||||
};
|
||||
// #docregion goto-detail
|
||||
DashboardComponent.prototype.gotoDetail = function (hero) {
|
||||
var link = ['HeroDetail', { id: hero.id }];
|
||||
this._router.navigate(link);
|
||||
};
|
||||
DashboardComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-dashboard',
|
||||
templateUrl: 'app/dashboard.component.html',
|
||||
// #docregion css
|
||||
styleUrls: ['app/dashboard.component.css']
|
||||
}),
|
||||
__metadata('design:paramtypes', [router_1.Router, hero_service_1.HeroService])
|
||||
], DashboardComponent);
|
||||
return DashboardComponent;
|
||||
})();
|
||||
exports_1("DashboardComponent", DashboardComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=dashboard.component.js.map
|
|
@ -0,0 +1,42 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from 'angular2/core';
|
||||
// #docregion import-router
|
||||
import { Router } from 'angular2/router';
|
||||
// #enddocregion import-router
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
templateUrl: 'app/dashboard.component.html',
|
||||
// #docregion css
|
||||
styleUrls: ['app/dashboard.component.css']
|
||||
// #enddocregion css
|
||||
})
|
||||
// #docregion component
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
heroes: Hero[] = [];
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private _router: Router,
|
||||
private _heroService: HeroService) {
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
ngOnInit() {
|
||||
this._heroService.getHeroes()
|
||||
.then(heroes => this.heroes = heroes.slice(1,5));
|
||||
}
|
||||
|
||||
// #docregion goto-detail
|
||||
gotoDetail(hero: Hero) {
|
||||
let link = ['HeroDetail', { id: hero.id }];
|
||||
this._router.navigate(link);
|
||||
}
|
||||
// #enddocregion goto-detail
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,25 @@
|
|||
/* #docregion */
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
margin: .5em 0;
|
||||
color: #607D8B;
|
||||
font-weight: bold;
|
||||
}
|
||||
input {
|
||||
height: 2em;
|
||||
font-size: 1em;
|
||||
padding-left: .4em;
|
||||
}
|
||||
button {
|
||||
margin-top: 20px;
|
||||
font-family: Arial;
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer; cursor: hand;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<!-- #docplaster -->
|
||||
<!-- #docregion -->
|
||||
<div *ngIf="hero">
|
||||
<h2>{{hero.name}} details!</h2>
|
||||
<div>
|
||||
<label>id: </label>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name: </label>
|
||||
<input [(ngModel)]="hero.name" placeholder="name" />
|
||||
</div>
|
||||
<!-- #docregion back-button -->
|
||||
<button (click)="goBack()">Back</button>
|
||||
<!-- #enddocregion back-button -->
|
||||
</div>
|
|
@ -0,0 +1,68 @@
|
|||
System.register(['angular2/core', 'angular2/router', './hero.service'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, router_1, hero_service_1;
|
||||
var HeroDetailComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
// #enddocregion import-hero-service
|
||||
// #docregion extract-template
|
||||
HeroDetailComponent = (function () {
|
||||
// #docregion ctor
|
||||
function HeroDetailComponent(_heroService, _routeParams) {
|
||||
this._heroService = _heroService;
|
||||
this._routeParams = _routeParams;
|
||||
}
|
||||
// #enddocregion ctor
|
||||
// #docregion ng-oninit
|
||||
HeroDetailComponent.prototype.ngOnInit = function () {
|
||||
var _this = this;
|
||||
// #docregion get-id
|
||||
var id = +this._routeParams.get('id');
|
||||
// #enddocregion get-id
|
||||
this._heroService.getHero(id)
|
||||
.then(function (hero) { return _this.hero = hero; });
|
||||
};
|
||||
// #enddocregion ng-oninit
|
||||
// #docregion go-back
|
||||
HeroDetailComponent.prototype.goBack = function () {
|
||||
window.history.back();
|
||||
};
|
||||
HeroDetailComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-hero-detail',
|
||||
// #docregion template-url
|
||||
templateUrl: 'app/hero-detail.component.html',
|
||||
// #enddocregion template-url
|
||||
// #enddocregion v2
|
||||
styleUrls: ['app/hero-detail.component.css'],
|
||||
inputs: ['hero']
|
||||
}),
|
||||
__metadata('design:paramtypes', [hero_service_1.HeroService, router_1.RouteParams])
|
||||
], HeroDetailComponent);
|
||||
return HeroDetailComponent;
|
||||
})();
|
||||
exports_1("HeroDetailComponent", HeroDetailComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion v2
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=hero-detail.component.js.map
|
|
@ -0,0 +1,57 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion v2
|
||||
// #docregion import-oninit
|
||||
import { Component, OnInit } from 'angular2/core';
|
||||
// #enddocregion import-oninit
|
||||
// #docregion import-route-params
|
||||
import {RouteParams} from 'angular2/router';
|
||||
// #enddocregion import-route-params
|
||||
|
||||
import { Hero } from './hero';
|
||||
// #docregion import-hero-service
|
||||
import { HeroService } from './hero.service';
|
||||
// #enddocregion import-hero-service
|
||||
|
||||
// #docregion extract-template
|
||||
@Component({
|
||||
selector: 'my-hero-detail',
|
||||
// #docregion template-url
|
||||
templateUrl: 'app/hero-detail.component.html',
|
||||
// #enddocregion template-url
|
||||
// #enddocregion v2
|
||||
styleUrls: ['app/hero-detail.component.css'],
|
||||
inputs: ['hero']
|
||||
// #docregion v2
|
||||
})
|
||||
// #enddocregion extract-template
|
||||
// #docregion implement
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
// #enddocregion implement
|
||||
hero: Hero;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private _heroService: HeroService,
|
||||
private _routeParams: RouteParams) {
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion ng-oninit
|
||||
ngOnInit() {
|
||||
// #docregion get-id
|
||||
let id = +this._routeParams.get('id');
|
||||
// #enddocregion get-id
|
||||
this._heroService.getHero(id)
|
||||
.then(hero => this.hero = hero);
|
||||
}
|
||||
// #enddocregion ng-oninit
|
||||
|
||||
// #docregion go-back
|
||||
goBack() {
|
||||
window.history.back();
|
||||
}
|
||||
// #enddocregion go-back
|
||||
}
|
||||
// #enddocregion v2
|
||||
// #enddocregion
|
|
@ -0,0 +1,8 @@
|
|||
System.register([], function(exports_1) {
|
||||
return {
|
||||
setters:[],
|
||||
execute: function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=hero.js.map
|
|
@ -0,0 +1,51 @@
|
|||
System.register(['./mock-heroes', 'angular2/core'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var mock_heroes_1, core_1;
|
||||
var HeroService;
|
||||
return {
|
||||
setters:[
|
||||
function (mock_heroes_1_1) {
|
||||
mock_heroes_1 = mock_heroes_1_1;
|
||||
},
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
HeroService = (function () {
|
||||
function HeroService() {
|
||||
}
|
||||
HeroService.prototype.getHeroes = function () {
|
||||
return Promise.resolve(mock_heroes_1.HEROES);
|
||||
};
|
||||
// See the "Take it slow" appendix
|
||||
HeroService.prototype.getHeroesSlowly = function () {
|
||||
return new Promise(function (resolve) {
|
||||
return setTimeout(function () { return resolve(mock_heroes_1.HEROES); }, 2000);
|
||||
} // 2 seconds
|
||||
// 2 seconds
|
||||
);
|
||||
};
|
||||
//#docregion get-hero
|
||||
HeroService.prototype.getHero = function (id) {
|
||||
return Promise.resolve(mock_heroes_1.HEROES).then(function (heroes) { return heroes.filter(function (hero) { return hero.id === id; })[0]; });
|
||||
};
|
||||
HeroService = __decorate([
|
||||
core_1.Injectable(),
|
||||
__metadata('design:paramtypes', [])
|
||||
], HeroService);
|
||||
return HeroService;
|
||||
})();
|
||||
exports_1("HeroService", HeroService);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=hero.service.js.map
|
|
@ -0,0 +1,28 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Hero } from './hero';
|
||||
import { HEROES } from './mock-heroes';
|
||||
import { Injectable } from 'angular2/core';
|
||||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
getHeroes() {
|
||||
return Promise.resolve(HEROES);
|
||||
}
|
||||
|
||||
// See the "Take it slow" appendix
|
||||
getHeroesSlowly() {
|
||||
return new Promise<Hero[]>(resolve =>
|
||||
setTimeout(()=>resolve(HEROES), 2000) // 2 seconds
|
||||
);
|
||||
}
|
||||
|
||||
//#docregion get-hero
|
||||
getHero(id: number) {
|
||||
return Promise.resolve(HEROES).then(
|
||||
heroes => heroes.filter(hero => hero.id === id)[0]
|
||||
);
|
||||
}
|
||||
//#enddocregion get-hero
|
||||
}
|
||||
// #enddocregion
|
|
@ -0,0 +1,4 @@
|
|||
export interface Hero {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* #docregion */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 10em;
|
||||
}
|
||||
.heroes li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0em;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
color: white;
|
||||
}
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #EEE;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0em 0.7em;
|
||||
background-color: #607D8B;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<!-- #docplaster -->
|
||||
<!-- #docregion -->
|
||||
<h2>My Heroes</h2>
|
||||
<ul class="heroes">
|
||||
<li *ngFor="#hero of heroes"
|
||||
[class.selected]="hero === selectedHero"
|
||||
(click)="onSelect(hero)">
|
||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
<!-- #docregion mini-detail -->
|
||||
<div *ngIf="selectedHero">
|
||||
<h2>
|
||||
<!-- #docregion pipe -->
|
||||
{{selectedHero.name | uppercase}} is my hero
|
||||
<!-- #enddocregion pipe -->
|
||||
</h2>
|
||||
<button (click)="gotoDetail()">View Details</button>
|
||||
</div>
|
||||
<!-- #enddocregion mini-detail -->
|
||||
<!-- #enddocregion -->
|
|
@ -0,0 +1,65 @@
|
|||
System.register(['angular2/core', 'angular2/router', './hero-detail.component', './hero.service'], function(exports_1) {
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var core_1, router_1, hero_detail_component_1, hero_service_1;
|
||||
var HeroesComponent;
|
||||
return {
|
||||
setters:[
|
||||
function (core_1_1) {
|
||||
core_1 = core_1_1;
|
||||
},
|
||||
function (router_1_1) {
|
||||
router_1 = router_1_1;
|
||||
},
|
||||
function (hero_detail_component_1_1) {
|
||||
hero_detail_component_1 = hero_detail_component_1_1;
|
||||
},
|
||||
function (hero_service_1_1) {
|
||||
hero_service_1 = hero_service_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
// #docregion metadata
|
||||
// #docregion heroes-component-renaming
|
||||
HeroesComponent = (function () {
|
||||
function HeroesComponent(_router, _heroService) {
|
||||
this._router = _router;
|
||||
this._heroService = _heroService;
|
||||
}
|
||||
HeroesComponent.prototype.getHeroes = function () {
|
||||
var _this = this;
|
||||
this._heroService.getHeroes().then(function (heroes) { return _this.heroes = heroes; });
|
||||
};
|
||||
HeroesComponent.prototype.ngOnInit = function () {
|
||||
this.getHeroes();
|
||||
};
|
||||
HeroesComponent.prototype.onSelect = function (hero) { this.selectedHero = hero; };
|
||||
HeroesComponent.prototype.gotoDetail = function () {
|
||||
this._router.navigate(['HeroDetail', { id: this.selectedHero.id }]);
|
||||
};
|
||||
HeroesComponent = __decorate([
|
||||
core_1.Component({
|
||||
selector: 'my-heroes',
|
||||
// #enddocregion heroes-component-renaming
|
||||
templateUrl: 'app/heroes.component.html',
|
||||
styleUrls: ['app/heroes.component.css'],
|
||||
directives: [hero_detail_component_1.HeroDetailComponent]
|
||||
}),
|
||||
__metadata('design:paramtypes', [router_1.Router, hero_service_1.HeroService])
|
||||
], HeroesComponent);
|
||||
return HeroesComponent;
|
||||
})();
|
||||
exports_1("HeroesComponent", HeroesComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion heroes-component-renaming
|
||||
// #enddocregion class
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=heroes.component.js.map
|
|
@ -0,0 +1,50 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from 'angular2/core';
|
||||
import { Router } from 'angular2/router';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
// #docregion metadata
|
||||
// #docregion heroes-component-renaming
|
||||
@Component({
|
||||
selector: 'my-heroes',
|
||||
// #enddocregion heroes-component-renaming
|
||||
templateUrl: 'app/heroes.component.html',
|
||||
styleUrls: ['app/heroes.component.css'],
|
||||
directives: [HeroDetailComponent]
|
||||
// #docregion heroes-component-renaming
|
||||
})
|
||||
// #enddocregion heroes-component-renaming
|
||||
// #enddocregion metadata
|
||||
// #docregion class
|
||||
// #docregion heroes-component-renaming
|
||||
export class HeroesComponent implements OnInit {
|
||||
// #enddocregion heroes-component-renaming
|
||||
heroes: Hero[];
|
||||
selectedHero: Hero;
|
||||
|
||||
constructor(
|
||||
private _router: Router,
|
||||
private _heroService: HeroService) { }
|
||||
|
||||
getHeroes() {
|
||||
this._heroService.getHeroes().then(heroes => this.heroes = heroes);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getHeroes();
|
||||
}
|
||||
|
||||
onSelect(hero: Hero) { this.selectedHero = hero; }
|
||||
|
||||
gotoDetail() {
|
||||
this._router.navigate(['HeroDetail', { id: this.selectedHero.id }]);
|
||||
}
|
||||
// #docregion heroes-component-renaming
|
||||
}
|
||||
// #enddocregion heroes-component-renaming
|
||||
// #enddocregion class
|
||||
// #enddocregion
|
|
@ -0,0 +1,16 @@
|
|||
System.register(['angular2/platform/browser', './app.component'], function(exports_1) {
|
||||
var browser_1, app_component_1;
|
||||
return {
|
||||
setters:[
|
||||
function (browser_1_1) {
|
||||
browser_1 = browser_1_1;
|
||||
},
|
||||
function (app_component_1_1) {
|
||||
app_component_1 = app_component_1_1;
|
||||
}],
|
||||
execute: function() {
|
||||
browser_1.bootstrap(app_component_1.AppComponent);
|
||||
}
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=main.js.map
|
|
@ -0,0 +1,4 @@
|
|||
import { bootstrap } from 'angular2/platform/browser';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
bootstrap(AppComponent);
|
|
@ -0,0 +1,22 @@
|
|||
System.register([], function(exports_1) {
|
||||
var HEROES;
|
||||
return {
|
||||
setters:[],
|
||||
execute: function() {
|
||||
exports_1("HEROES", HEROES = [
|
||||
{ "id": 11, "name": "Mr. Nice" },
|
||||
{ "id": 12, "name": "Narco" },
|
||||
{ "id": 13, "name": "Bombasto" },
|
||||
{ "id": 14, "name": "Celeritas" },
|
||||
{ "id": 15, "name": "Magneta" },
|
||||
{ "id": 16, "name": "RubberMan" },
|
||||
{ "id": 17, "name": "Dynama" },
|
||||
{ "id": 18, "name": "Dr IQ" },
|
||||
{ "id": 19, "name": "Magma" },
|
||||
{ "id": 20, "name": "Tornado" }
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
||||
//# sourceMappingURL=mock-heroes.js.map
|
|
@ -0,0 +1,16 @@
|
|||
// #docregion
|
||||
import { Hero } from './hero';
|
||||
|
||||
export var HEROES: Hero[] = [
|
||||
{"id": 11, "name": "Mr. Nice"},
|
||||
{"id": 12, "name": "Narco"},
|
||||
{"id": 13, "name": "Bombasto"},
|
||||
{"id": 14, "name": "Celeritas"},
|
||||
{"id": 15, "name": "Magneta"},
|
||||
{"id": 16, "name": "RubberMan"},
|
||||
{"id": 17, "name": "Dynama"},
|
||||
{"id": 18, "name": "Dr IQ"},
|
||||
{"id": 19, "name": "Magma"},
|
||||
{"id": 20, "name": "Tornado"}
|
||||
];
|
||||
// #enddocregion
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- #docregion head -->
|
||||
<!-- #docregion base-href -->
|
||||
<head>
|
||||
<base href="/">
|
||||
<!-- #enddocregion base-href -->
|
||||
<title>Angular 2 Tour of Heroes</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- #enddocregion head -->
|
||||
<!-- #docregion css -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- #enddocregion css -->
|
||||
<!-- #docregion head -->
|
||||
<!-- IE required polyfills, in this exact order -->
|
||||
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
|
||||
|
||||
<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 -->
|
||||
<script src="node_modules/angular2/bundles/router.dev.js"></script>
|
||||
<!-- #enddocregion router -->
|
||||
<script>
|
||||
System.config({packages: {app: {format: 'register', defaultExtension: 'js'}}});
|
||||
System.import('app/main')
|
||||
.then(null, console.error.bind(console));
|
||||
</script>
|
||||
</head>
|
||||
<!-- #enddocregion head -->
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"description": "Tour of Heroes: Part 5",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2].*"
|
||||
],
|
||||
"tags": ["tutorial", "tour", "heroes", "router"]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/* #docplaster */
|
||||
/* #docregion */
|
||||
h2 { 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}
|
||||
* { font-family: Arial; }
|
|
@ -18,6 +18,7 @@ export class DashboardComponent implements OnInit {
|
|||
}
|
||||
|
||||
gotoDetail(hero: Hero) {
|
||||
this._router.navigate(['HeroDetail', { id: hero.id }]);
|
||||
let link = ['HeroDetail', { id: hero.id }];
|
||||
this._router.navigate(link);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {Component, OnInit} from 'angular2/core';
|
||||
import {RouteParams} from 'angular2/router';
|
||||
|
||||
import {Hero} from './hero';
|
||||
import {HeroService} from './hero.service';
|
||||
|
||||
|
@ -17,10 +18,8 @@ export class HeroDetailComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.hero) {
|
||||
let id = +this._routeParams.get('id');
|
||||
this._heroService.getHero(id).then(hero => this.hero = hero);
|
||||
}
|
||||
let id = +this._routeParams.get('id');
|
||||
this._heroService.getHero(id).then(hero => this.hero = hero);
|
||||
}
|
||||
|
||||
goBack() {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import {Injectable} from 'angular2/core';
|
||||
import {HEROES} from './mock-heroes';
|
||||
import {HEROES} from './mock-heroes';
|
||||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
getHeroes() {
|
||||
return Promise.resolve(HEROES);
|
||||
}
|
||||
getHeroes() {
|
||||
return Promise.resolve(HEROES);
|
||||
}
|
||||
|
||||
getHero(id: number) {
|
||||
return Promise.resolve(HEROES)
|
||||
.then(heroes => heroes.filter(h => h.id === id)[0]);
|
||||
}
|
||||
return Promise.resolve(HEROES).then(
|
||||
heroes => heroes.filter(hero => hero.id === id)[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,13 @@ include ../../../../_includes/_util-fns
|
|||
up to a modular, multi-view design with child routes.
|
||||
|
||||
Try that [live final version](/resources/live-examples/router/ts/plnkr.html).
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
In plunker, to see routing changes in the browser address bar,
|
||||
pop out the preview window by clicking this blue button in the upper right corner:
|
||||
|
||||
<img src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" width='200'/>
|
||||
:marked
|
||||
But first, an overview of router basics.
|
||||
|
||||
.l-main-section
|
||||
|
|
|
@ -204,7 +204,8 @@ figure.image-display
|
|||
We shouldn't expect `json()` to return the heroes array directly.
|
||||
The server we're calling always wraps JSON results in an object with a `data`
|
||||
property. We have to unwrap it to get the heroes.
|
||||
This is conventional web api behavior, driven by security concerns.
|
||||
This is conventional web api behavior, driven by
|
||||
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
|
||||
:marked
|
||||
### Do not return the response object
|
||||
Our `getHeroes()` could have returned the `Observable<Response>`.
|
||||
|
|
|
@ -20,5 +20,9 @@
|
|||
"toh-pt4": {
|
||||
"title": "Services",
|
||||
"intro": "We create a reusable service to manage our hero data calls"
|
||||
},
|
||||
"toh-pt5": {
|
||||
"title": "Routing",
|
||||
"intro": "We add the Angular Component Router and learn to navigate among the views"
|
||||
}
|
||||
}
|
|
@ -110,7 +110,7 @@ code-example(format="." language="bash").
|
|||
We already have mock `Hero` data sitting in the `AppComponent`. It doesn't belong there. It doesn't belong *here* either.
|
||||
We'll move the mock data to its own file.
|
||||
|
||||
Cut the the `HEROES` array from `app.component.ts` and paste it to a new file in the `app` folder named `mock-heroes.ts`.
|
||||
Cut the `HEROES` array from `app.component.ts` and paste it to a new file in the `app` folder named `mock-heroes.ts`.
|
||||
We copy the `import {Hero} ...` statement as well because the heroes array uses the `Hero` interface.
|
||||
|
||||
+makeExample('toh-4/ts/app/mock-heroes.ts', null, 'mock-heroes.ts (Heroes array)')
|
||||
|
|
|
@ -0,0 +1,686 @@
|
|||
include ../../../../_includes/_util-fns
|
||||
|
||||
:marked
|
||||
# Routing Around the App
|
||||
We received new requirements for our Tour of Heroes application:
|
||||
* add a *Dashboard* view.
|
||||
* navigate between the *Heroes* and *Dashboard* views.
|
||||
* clicking on a hero in either view navigates to a detail view of the selected hero.
|
||||
* clicking a *deep link* in an email opens the detail view for a particular hero;
|
||||
|
||||
When we’re done, users will be able to navigate the app like this:
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations")
|
||||
:marked
|
||||
We'll add Angular’s *Component Router* to our app to satisfy these requirements.
|
||||
.l-sub-section
|
||||
:marked
|
||||
The [Routing and Navigation](../guide/router.html) chapter covers the router in more detail
|
||||
than we will in this tour.
|
||||
:marked
|
||||
[Run the live example](/resources/live-examples/toh-5/ts/plnkr.html).
|
||||
.l-sub-section
|
||||
:marked
|
||||
In plunker, to see routing changes in the browser address bar,
|
||||
pop out the preview window by clicking this blue button in the upper right corner:
|
||||
|
||||
<img src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" width='200'/>
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Where We Left Off
|
||||
Before we continue with our Tour of Heroes, let’s verify that we have the following structure after adding our hero service
|
||||
and hero detail component. If not, we’ll need to go back and follow the previous chapters.
|
||||
|
||||
.filetree
|
||||
.file angular2-tour-of-heroes
|
||||
.children
|
||||
.file node_modules
|
||||
.file app
|
||||
.children
|
||||
.file app.component.ts
|
||||
.file hero.ts
|
||||
.file hero-detail.component.ts
|
||||
.file hero.service.ts
|
||||
.file main.ts
|
||||
.file mock-heroes.ts
|
||||
.file index.html
|
||||
.file package.json
|
||||
.file tsconfig.json
|
||||
:marked
|
||||
### Keep the app transpiling and running
|
||||
Open a terminal/console window and enter the following command to
|
||||
start the TypeScript compiler, start the server, and watch for changes:
|
||||
|
||||
code-example(format="." language="bash").
|
||||
npm start
|
||||
|
||||
:marked
|
||||
The application runs and updates automatically as we continue to build the Tour of Heroes.
|
||||
|
||||
## Action plan
|
||||
Here's our plan
|
||||
|
||||
* turn `AppComponent` into an application shell that only handles navigation.
|
||||
* relocate the *Heroes* concerns within the current `AppComponent` to a separate `HeroesComponent`
|
||||
* add routing
|
||||
* create a new `DashboardComponent`
|
||||
* tie the *Dashboard* into the navigation structure.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
*Routing* is another name for *navigation*. The *router* is the mechanism for navigating from view to view.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Splitting the *AppComponent*
|
||||
|
||||
Our current app loads `AppComponent` and immediately displays the list of heroes.
|
||||
|
||||
Our revised app should present a shell with a choice of views (*Dashboard* and *Heroes*) and then default to one of them.
|
||||
|
||||
The `AppComponent` should only handle navigation.
|
||||
Let's move the display of *Heroes* out of `AppComponent` and into its own `HeroesComponent`.
|
||||
|
||||
### *HeroesComponent*
|
||||
`AppComponent` is already dedicated to *Heroes*.
|
||||
Instead of moving anything out of `AppComponent`, we'll just rename it `HeroesComponent`
|
||||
and create a new `AppComponent` shell separately.
|
||||
|
||||
The steps are:
|
||||
* rename `app.component.ts` file to `heroes.component.ts`.
|
||||
* rename the `AppComponent` class to `HeroesComponent`.
|
||||
* rename the selector `my-app` to `my-heroes`.
|
||||
|
||||
:marked
|
||||
+makeExample('toh-5/ts/app/heroes.component.ts', 'heroes-component-renaming', 'app/heroes.component.ts (renaming)')(format=".")
|
||||
|
||||
:marked
|
||||
## Create *AppComponent*
|
||||
The new `AppComponent` will be the application shell.
|
||||
It will have some navigation links at the top and a display area below for the pages we navigate to.
|
||||
|
||||
The initial steps are:
|
||||
|
||||
* create a new file named `app.component.ts`.
|
||||
* define an `AppComponent` class.
|
||||
* `export` it so we can reference it during bootstrapping in `main.ts`.
|
||||
* expose an application `title` property.
|
||||
* add the `@Component` metadata decorator above the class with a `my-app` selector.
|
||||
* add a template with `<h1>` tags surrounding a binding to the `title` property.
|
||||
* add the `<my-heroes>` tags to the template so we still see the heroes.
|
||||
* add the `HeroesComponent` to the `directives` array so Angular recognizes the `<my-heroes>` tags.
|
||||
* add the `HeroService` to the `providers` array because we'll need it in every other view.
|
||||
* add the supporting `import` statements.
|
||||
|
||||
Our first draft looks like this:
|
||||
+makeExample('toh-5/ts/app/app.component.1.ts', null, 'app/app.component.ts (v1)')
|
||||
:marked
|
||||
.callout.is-critical
|
||||
header Remove <i>HeroService</i> from the <i>HeroesComponent</i> providers
|
||||
:marked
|
||||
Go back to the `HeroesComponent` and **remove the `HeroService`** from its `providers` array.
|
||||
We are *promoting* this service from the `HeroesComponent` to the `AppComponent`.
|
||||
We ***do not want two copies*** of this service at two different levels of our app.
|
||||
:marked
|
||||
The app still runs and still displays heroes.
|
||||
Our refactoring of `AppComponent` into a new `AppComponent` and a `HeroesComponent` worked!
|
||||
We have done no harm.
|
||||
|
||||
:marked
|
||||
## Add Routing
|
||||
|
||||
We're ready to take the next step.
|
||||
Instead of displaying heroes automatically, we'd like to show them *after* the user clicks a button.
|
||||
In other words, we'd like to navigate to the list of heroes.
|
||||
|
||||
We'll need the Angular *Component Router*.
|
||||
|
||||
### Include the Router Library
|
||||
Not all apps need routing which is why the Angular *Component Router* is in a separate, optional module library.
|
||||
|
||||
Our Tour of Heroes needs routing,
|
||||
so we load the library in the `index.html` in a script tag immediately *after* the angular script itself.
|
||||
+makeExample('toh-5/ts/index.html', 'router', 'index.html (router)')(format=".")
|
||||
:marked
|
||||
While we're in `index.html`, we add `<base href="/">` at the top of the `<head>` section.
|
||||
+makeExample('toh-5/ts/index.html', 'base-href', 'index.html (base href)')(format=".")
|
||||
.callout.is-important
|
||||
header base href is essential
|
||||
:marked
|
||||
See the *base href* section of the [Router](../guide/router.html#!#base-href) chapter to learn why this matters.
|
||||
:marked
|
||||
### Make the router available.
|
||||
The *Component Router* is a service. Like any service, we have to import it and make it
|
||||
available to the application by adding it to the `providers` array.
|
||||
|
||||
The Angular router is a combination of multiple services (`ROUTER_PROVIDERS`), multiple directives (`ROUTER_DIRECTIVES`),
|
||||
and a configuration decorator (`RouteConfig`). We'll import them all together:
|
||||
+makeExample('toh-5/ts/app/app.component.2.ts', 'import-router', 'app.component.ts (router imports)')(format=".")
|
||||
:marked
|
||||
Next we update the `directives` and `providers` metadata arrays to *include* the router assets.
|
||||
+makeExample('toh-5/ts/app/app.component.2.ts', 'directives-and-providers', 'app.component.ts (directives and providers)')(format=".")
|
||||
:marked
|
||||
Notice that we also removed the `HeroesComponent` from the `directives` array.
|
||||
`AppComponent` no longer shows heroes; that will be the router's job.
|
||||
We'll soon remove `<my-heroes>` from the template too.
|
||||
|
||||
### Add and configure the router
|
||||
|
||||
The `AppComponent` doesn't have a router yet. We'll use the `@RouteConfig` decorator to simultaneously
|
||||
(a) assign a router to the component and (b) configure that router with *routes*.
|
||||
|
||||
*Routes* tell the router which views to display when a user clicks a link or
|
||||
pastes a URL into the browser address bar.
|
||||
|
||||
Let's define our first route, a route to the `HeroesComponent`.
|
||||
+makeExample('toh-5/ts/app/app.component.2.ts', 'route-config', 'app.component.ts (RouteConfig for heroes)')(format=".")
|
||||
:marked
|
||||
`@RouteConfig` takes an array of *route definitions*.
|
||||
We have only one route definition at the moment but rest assured, we'll add more.
|
||||
|
||||
This *route definition* has three parts:
|
||||
* **path**: the router matches this route's path to the URL in the browser address bar (`/heroes`).
|
||||
|
||||
* **name**: the official name of the route; it *must* begin with a capital letter to avoid confusion with the *path* (`Heroes`).
|
||||
|
||||
* **component**: the component that the router should create when navigating to this route (`HeroesComponent`).
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn more about defining routes with @RouteConfig in the [Routing](../guide/router.html) chapter.
|
||||
:marked
|
||||
### Router Outlet
|
||||
|
||||
If we paste the path, `/heroes`, into the browser address bar,
|
||||
the router should match it to the `'Heroes'` route and display the `HeroesComponent`.
|
||||
But where?
|
||||
|
||||
We have to ***tell it where*** by adding `<router-outlet>` marker tags to the bottom of the template.
|
||||
`RouterOutlet` is one of the `ROUTER_DIRECTIVES`.
|
||||
The router displays each component immediately below the `<router-outlet>` as we navigate through the application.
|
||||
|
||||
### Router Links
|
||||
We don't really expect users to paste a route URL into the address bar.
|
||||
We add an anchor tag to the template which, when clicked, triggers navigation to the `HeroesComponent`.
|
||||
|
||||
The revised template looks like this:
|
||||
+makeExample('toh-5/ts/app/app.component.2.ts', 'template', 'app.component.ts (template for Heroes)')(format=".")
|
||||
:marked
|
||||
Notice the `[routerLink]` binding in the anchor tag.
|
||||
We bind the `RouterLink` directive (another of the `ROUTER_DIRECTIVES`) to an array
|
||||
that tells the router where to navigate when the user clicks the link.
|
||||
|
||||
We define a *routing instruction* with a *link parameters array*.
|
||||
The array only has one element in our little sample, the quoted ***name* of the route** to follow.
|
||||
Looking back at the route configuration, we confirm that `'Heroes'` is the name of the route to the `HeroesComponent`.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn about the *link parameters array* in the [Routing](../guide/router.htmll#link-parameters-array) chapter.
|
||||
:marked
|
||||
Refresh the browser. We see only the app title. We don't see the heroes list.
|
||||
.l-sub-section
|
||||
:marked
|
||||
The browser's address bar shows `/`.
|
||||
The route path to `HeroesComponent` is `/heroes`, not `/`.
|
||||
We don't have a route that matches the path `/`, so there is nothing to show.
|
||||
That's something we'll want to fix.
|
||||
:marked
|
||||
We click the "Heroes" navigation link, the browser bar updates to `/heroes`,
|
||||
and now we see the list of heroes. We are navigating at last!
|
||||
|
||||
At this stage, our `AppComponent` looks like this.
|
||||
+makeExample('toh-5/ts/app/app.component.2.ts',null, 'app/app.component.ts (v2)')
|
||||
:marked
|
||||
The *AppComponent* is now attached to a router and displaying routed views.
|
||||
For this reason and to distinguish it from other kinds of components,
|
||||
we call this type of component a *Router Component*.
|
||||
|
||||
|
||||
:marked
|
||||
## Add a *Dashboard*
|
||||
Routing only makes sense when we have multiple views. We need another view.
|
||||
|
||||
Create a placeholder `DashboardComponent` that gives us something to navigate to and from.
|
||||
+makeExample('toh-5/ts/app/dashboard.component.1.ts',null, 'app/dashboard.component.ts (v1)')(format=".")
|
||||
:marked
|
||||
We’ll come back and make it more useful later.
|
||||
|
||||
### Configure the dashboard route
|
||||
Go back to `app.component.ts` and teach it to navigate to the dashboard.
|
||||
|
||||
Import the `DashboardComponent` so we can reference it in the dashboard route definition.
|
||||
|
||||
Add the following `'Dashboard'` route definition to the `@RouteConfig` array of definitions.
|
||||
+makeExample('toh-5/ts/app/app.component.ts','dashboard-route', 'app.component.ts (Dashboard Route)')(format=".")
|
||||
.l-sub-section
|
||||
:marked
|
||||
**useAsDefault**
|
||||
|
||||
We want the app to show the dashboard when it starts and
|
||||
we want to see a nice URL in the browser address bar that says `/dashboard`.
|
||||
Remember that the browser launches with `/` in the address bar.
|
||||
We don't have a route for that path and we'd rather not create one.
|
||||
|
||||
Fortunately we can add the `useAsDefault: true` property to the *route definition* and the
|
||||
router will display the dashboard when the browser URL doesn't match an existing route.
|
||||
:marked
|
||||
Finally, add a dashboard navigation link to the template, just above the *Heroes* link.
|
||||
|
||||
+makeExample('toh-5/ts/app/app.component.ts','template', 'app.component.ts (template)')(format=".")
|
||||
.l-sub-section
|
||||
:marked
|
||||
We nestled the two links within `<nav>` tags.
|
||||
They don't do anything yet but they'll be convenient when we style the links a little later in the chapter.
|
||||
:marked
|
||||
Refresh the browser. The app displays the dashboard and
|
||||
we can navigate between the dashboard and the heroes.
|
||||
|
||||
## Dashboard Top Heroes
|
||||
Let’s spice up the dashboard by displaying the top four heroes at a glance.
|
||||
|
||||
Replace the `template` metadata with a `templateUrl` property that points to a new
|
||||
template file, `dashboard.component.html`.
|
||||
|
||||
Create that file with these contents:
|
||||
+makeExample('toh-5/ts/app/dashboard.component.html', null, 'dashboard.component.html')(format=".")
|
||||
:marked
|
||||
We use `*ngFor` once again to iterate over a list of heroes and display their names.
|
||||
We added extra `<div>` elements to help with styling later in this chapter.
|
||||
|
||||
There's a `(click)` binding to a `gotoDetail` method we haven't written yet and
|
||||
we're displaying a list of heroes that we don't have.
|
||||
We have work to do, starting with those heroes.
|
||||
|
||||
### Share the *HeroService*
|
||||
|
||||
We'd like to re-use the `HeroService` to populate the component's `heroes` array.
|
||||
|
||||
Recall earlier in the chapter that we removed the `HeroService` from the `providers` array of the `HeroesComponent`
|
||||
and added it to the `providers` array of the top level `AppComponent`.
|
||||
|
||||
That move created a singleton `HeroService` instance, available to *all* components of the application.
|
||||
We'll inject and use it here in the `DashboardComponent` .
|
||||
|
||||
### Get heroes
|
||||
Open the `dashboard.component.ts` and add the requisite `import` statements.
|
||||
+makeExample('toh-5/ts/app/dashboard.component.2.ts','imports', 'app/dashboard.component.ts (imports)')(format=".")
|
||||
:marked
|
||||
We need `OnInit` interface because we'll initialize the heroes in the `ngOnInit` method as we've done before.
|
||||
We need the `Hero` and `HeroService` symbols in order to reference those types.
|
||||
|
||||
Now implement the `DashboardComponent` class like this:
|
||||
+makeExample('toh-5/ts/app/dashboard.component.2.ts','component', 'app/dashboard.component.ts (class)')
|
||||
:marked
|
||||
We saw this kind of logic before in the `HeroesComponent`.
|
||||
* create a `heroes` array property
|
||||
* inject the `HeroService` in the constructor and hold it in a private `_heroService` field.
|
||||
* call the service to get heroes inside the Angular `ngOnInit` lifecycle hook.
|
||||
|
||||
The noteworthy differences: we cherry-pick four heroes (2nd, 3rd, 4th, and 5th) with *slice*
|
||||
and stub the `gotoDetail` method until we're ready to implement it.
|
||||
|
||||
Refresh the browser and see four heroes in the new dashboard.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Navigate to Hero Details
|
||||
|
||||
Although we display the details of a selected hero at the bottom of the `HeroesComponent`,
|
||||
we don't yet *navigate* to the `HeroDetailComponent` in the three ways specified in our requirements:
|
||||
1. from the *Dashboard* to a selected hero.
|
||||
1. from the *Heroes* list to a selected hero.
|
||||
1. from a "deep link" URL pasted into the browser address bar.
|
||||
|
||||
Adding a `'HeroDetail'` route seem an obvious place to start.
|
||||
|
||||
### Routing to a hero detail
|
||||
|
||||
We'll add a route to the `HeroDetailComponent` in the `AppComponent` where our other routes are configured.
|
||||
|
||||
The new route is a bit unusual in that we must tell the `HeroDetailComponent` *which hero to show*.
|
||||
We didn't have to tell the `HeroesComponent` or the `DashboardComponent` anything.
|
||||
|
||||
At the moment the parent `HeroesComponent` sets the component's `hero` property to a hero object with a binding like this.
|
||||
code-example(format='').
|
||||
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
|
||||
:marked
|
||||
That clearly won't work in any of our routing scenarios.
|
||||
Certainly not the last one; we can't embed an entire hero object in the URL! Nor would we want to.
|
||||
|
||||
### Parameterized route
|
||||
We *can* add the hero's `id` to the URL. When routing to the hero whose `id` is 11, we could expect to see an URL such as this:
|
||||
code-example(format='').
|
||||
/detail/11
|
||||
:marked
|
||||
The `/detail/` part of that URL is constant. The trailing numeric `id` part changes from hero to hero.
|
||||
We need to represent that variable part of the route with a *parameter* (or *token*) that stands for the hero's `id`.
|
||||
|
||||
### Configure a Route with a Parameter
|
||||
|
||||
Here's the *route definition* we'll use.
|
||||
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (Route to HeroDetailComponent)')(format=".")
|
||||
:marked
|
||||
The colon (:) in the path indicates that `:id` is a placeholder to be filled with a specific hero `id`
|
||||
when navigating to the `HeroDetailComponent`.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Of course we have to import the `HeroDetailComponent` before we create this route:
|
||||
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-import')(format=".")
|
||||
:marked
|
||||
We're finished with the `AppComponent`.
|
||||
|
||||
We won't add a `'Hero Detail'` link to the template because users
|
||||
don't click a navigation *link* to view a particular hero.
|
||||
They click a *hero* whether that hero is displayed on the dashboard or in the heroes list.
|
||||
|
||||
We'll get to those *hero* clicks later in the chapter.
|
||||
There's no point in working on them until the `HeroDetailComponent`
|
||||
is ready to be navigated *to*.
|
||||
|
||||
That will require an `HeroDetailComponent` overhaul.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Revise the *HeroDetailComponent*
|
||||
|
||||
Before we rewrite the `HeroDetailComponent`, let's remember what it looks like now:
|
||||
+makeExample('toh-4/ts/app/hero-detail.component.ts', null, 'app/hero-detail.component.ts (current)')
|
||||
:marked
|
||||
The template won't change. We'll display a hero the same way. The big changes are driven by how we get the hero.
|
||||
|
||||
We will no longer receive the hero in a parent component property binding.
|
||||
The new `HeroDetailComponent` should take the `id` parameter from the router's `RouteParams` service
|
||||
and use the `HeroService` to fetch the hero with that `id` from storage.
|
||||
|
||||
We need an import statement to reference the `RouteParams`.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-route-params')(format=".")
|
||||
:marked
|
||||
We import the `HeroService`so we can fetch a hero`.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-hero-service')(format=".")
|
||||
:marked
|
||||
We import the `OnInit` interface because we'll call the `HeroService` inside the `ngOnInit` component lifecycle hook.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-oninit')(format=".")
|
||||
:marked
|
||||
We inject the both the `RouteParams` service and the `HeroService` into the constructor as we've done before,
|
||||
making private variables for both:
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'ctor', 'app/hero-detail.component.ts (constructor)')(format=".")
|
||||
:marked
|
||||
Inside the `ngOnInit` lifecycle hook, extract the `id` parameter value from the `RouteParams` service
|
||||
and use the `HeroService` to fetch the hero with that `id`.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'ng-oninit', 'app/hero-detail.component.ts (ngOnInit)')(format=".")
|
||||
:marked
|
||||
Notice how we extract the `id` by calling the `RouteParams.get` method.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'get-id')(format=".")
|
||||
:marked
|
||||
The hero `id` is a number. Route parameters are *always strings*.
|
||||
So we convert the route parameter value to a number with the JavaScript (+) operator.
|
||||
|
||||
### Add *HeroService.getHero*
|
||||
The problem with this bit of code is that `HeroService` doesn't have a `getHero` method!
|
||||
We better fix that quickly before someone notices that we broke the app.
|
||||
|
||||
Open `HeroService` and add the `getHero` method. It's trivial given that we're still faking data access:
|
||||
+makeExample('toh-5/ts/app/hero.service.ts', 'get-hero', 'app/hero.service.ts (getHero)')(format=".")
|
||||
:marked
|
||||
Return to the `HeroDetailComponent` to clean up loose ends.
|
||||
|
||||
### Find our way back
|
||||
|
||||
We can navigate *to* the `HeroDetailComponent` in several ways.
|
||||
How do we navigate somewhere else when we're done?
|
||||
|
||||
The user could click one of the two links in the `AppComponent`. Or click the browser's back button.
|
||||
We'll add a third option, a `goBack` method that navigates backward one step in the browser's history stack
|
||||
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'go-back', 'app/hero-detail.component.ts (goBack)')(format=".")
|
||||
.l-sub-section
|
||||
:marked
|
||||
Going back too far could take us out of the application.
|
||||
That's acceptable in a demo. We'd guard against it in a real application,
|
||||
perhaps with the [*routerCanDeactivate* hook](/docs/ts/latest/api/router/CanDeactivate-interface.html).
|
||||
:marked
|
||||
Then we wire this method with an event binding to a *Back* button that we add to the bottom of the component template.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.html', 'back-button')(format=".")
|
||||
:marked
|
||||
Modifing the template to add this button spurs us to take one more incremental improvement and migrate the template to its own file
|
||||
called `hero-detail.component.html`
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.html', '', 'app/hero-detail.component.html')(format=".")
|
||||
:marked
|
||||
We update the component metadata with a `templateUrl` pointing to the template file that we just created.
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'template-url', 'app/hero-detail.component.ts (templateUrl)')(format=".")
|
||||
:marked
|
||||
Here's the (nearly) finished `HeroDetailComponent`:
|
||||
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'v2', 'app/hero-detail.component.ts (latest)')(format=".")
|
||||
:marked
|
||||
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Select a *Dashboard* Hero
|
||||
When a user selects a hero in the dashboard, the app should navigate to the `HeroDetailComponent` to view and edit the selected hero..
|
||||
|
||||
In the dashboard template we bound each hero's click event to the `gotoDetail` method, passing along the selected `hero` entity.
|
||||
+makeExample('toh-5/ts/app/dashboard.component.html','click', 'app/dashboard.component.html (click binding)')(format=".")
|
||||
:marked
|
||||
We stubbed the `gotoDetail` method when we rewrote the `DashboardComponent`.
|
||||
Now we give it a real implementation.
|
||||
+makeExample('toh-5/ts/app/dashboard.component.ts','goto-detail', 'app/dashboard.component.ts (gotoDetail)')(format=".")
|
||||
:marked
|
||||
The `gotoDetail` method navigates in two steps:
|
||||
1. set a route *link parameters array*
|
||||
1. pass the array to the router's navigate method.
|
||||
|
||||
We wrote *link parameters arrays* in the `AppComponent` for the navigation links.
|
||||
Those arrays had only one element, the name of the destination route.
|
||||
|
||||
This array has two elements, the ***name*** of the destination route and a ***route parameter object***
|
||||
with an `id` field set to the value of the selected hero's `id`.
|
||||
|
||||
The two array items align with the ***name*** and ***:id*** token in the parameterized `HeroDetail` route configuration we added to `AppComponent` earlier in the chapter.
|
||||
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (hero detail route)')(format=".")
|
||||
:marked
|
||||
The `DashboardComponent` doesn't have the router yet. We obtain it in the usual way:
|
||||
`import` the `router` reference and inject it in the constructor (along with the `HeroService`):
|
||||
|
||||
+makeExample('toh-5/ts/app/dashboard.component.ts','import-router', 'app/dashboard.component.ts (excerpts)')(format=".")
|
||||
+makeExample('toh-5/ts/app/dashboard.component.ts','ctor')(format=".")
|
||||
:marked
|
||||
Refresh the browser and select a hero from the dashboard; the app should navigate directly to that hero’s details.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Select a Hero in the *HeroesComponent*
|
||||
We'll do something similar in the `HeroesComponent`.
|
||||
|
||||
That component's current template exhibits a "master/detail" style with the list of heroes
|
||||
at the top and details of the selected hero below.
|
||||
+makeExample('toh-4/ts/app/app.component.ts','template', 'app/heroes.component.ts (current template)')(format=".")
|
||||
:marked
|
||||
Delete the last line of the template with the `<my-hero-detail>` tags.
|
||||
|
||||
We'll no longer show the full `HeroDetailComponent` here.
|
||||
We're going to display the hero detail on its own page and route to it as we did in the dashboard.
|
||||
|
||||
But we'll throw in a small twist for variety.
|
||||
When the user selects a hero from the list, we *won't* go to the detail page.
|
||||
We'll show a *mini-detail* on *this* page instead and make the user click a button to navigate to the *full detail *page.
|
||||
|
||||
### Add the *mini-detail*
|
||||
Add the following HTML fragment at the bottom of the template where the `<my-hero-detail>` used to be:
|
||||
+makeExample('toh-5/ts/app/heroes.component.html','mini-detail')(format=".")
|
||||
:marked
|
||||
After clicking a hero, the user should see something like this below the hero list:
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/mini-hero-detail.png' alt="Mini Hero Detail" height="70")
|
||||
:marked
|
||||
### Format with the *UpperCasePipe*
|
||||
|
||||
Notice that the hero's name is displayed in CAPITAL LETERS. That's the effect of the `UpperCasePipe`
|
||||
that we slipped into the interpolation binding. Look for it right after the pipe operator, ( | ).
|
||||
+makeExample('toh-5/ts/app/heroes.component.html','pipe')(format=".")
|
||||
:marked
|
||||
Pipes are a good way to format strings, currency amounts, dates and other display data.
|
||||
Angular ships with several pipes and we can write our own.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn about pipes in the [Pipes](../guide/pipes.html) chapter.
|
||||
:marked
|
||||
### Move content out of the component file
|
||||
We are not done. We still have to update the component class to support navigation to the
|
||||
`HeroDetailComponent` when the user clicks the *View Details* button.
|
||||
|
||||
This component file is really big. Most of it is either template or CSS styles.
|
||||
It's difficult to find the component logic amidst the noise of HTML and CSS.
|
||||
|
||||
Let's migrate the template and the styles to their own files before we make any more changes:
|
||||
1. *Cut-and-paste* the template contents into a new `heroes.component.html` file.
|
||||
1. *Cut-and-paste* the styles contents into a new `heroes.component.css` file.
|
||||
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
|
||||
|
||||
The revised component data looks like this:
|
||||
+makeExample('toh-5/ts/app/heroes.component.ts', 'metadata', 'app/heroes.component.ts (revised metadata)')(format=".")
|
||||
:marked
|
||||
Now we can see what's going on as we update the component class along the same lines as the dashboard:
|
||||
1. Import the `router`
|
||||
1. Inject the `router` in the constructor (along with the `HeroService`)
|
||||
1. Implement the `gotoDetail` method by calling the `router.navigate` method
|
||||
with a two-part 'HeroDetail' *link parameters array*.
|
||||
|
||||
Here's the revised component class:
|
||||
+makeExample('toh-5/ts/app/heroes.component.ts', 'class', 'app/heroes.component.ts (class)')
|
||||
:marked
|
||||
Refresh the browser and start clicking.
|
||||
We can navigate around the app, from the dashboard to hero details and back,
|
||||
for heroes list to the mini-detail to the hero details and back to the heroes again.
|
||||
We can jump back and forth between the dashboard and the heroes.
|
||||
|
||||
We've met all of the navigational requirements that propelled this chapter.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Styling the App
|
||||
The app is functional but pretty ugly.
|
||||
Our creative designer team provided some CSS files to make it look better.
|
||||
|
||||
### A Dashboard with Style
|
||||
The designers think we should display the dashboard heroes in a row of rectangles.
|
||||
They've given us ~60 lines of CSS for this purpose including some simple media queries for responsive design.
|
||||
|
||||
If we paste these ~60 lines into the component `styles` metadata,
|
||||
they'll completely obscure the component logic.
|
||||
Let's not do that. It's easier to edit CSS in a separate `*.css` file anyway.
|
||||
|
||||
Add a `dashboard.component.css` file to the `app` folder and reference
|
||||
that file in the component metadata's `styleUrls` array property like this:
|
||||
+makeExample('toh-5/ts/app/dashboard.component.ts', 'css', 'app/dashboard.component.ts (styleUrls)')(format=".")
|
||||
:marked
|
||||
.l-sub-section
|
||||
:marked
|
||||
The `styleUrls` property is an array of style file names (with paths).
|
||||
We could list multiple style files from different locations if we needed them.
|
||||
:marked
|
||||
### Stylish Hero Details
|
||||
The designers also gave us CSS styles specifically for the `HeroDetailComponent`.
|
||||
|
||||
Add a `hero-detail.component.css` to the `app` folder and refer to that file inside
|
||||
the `styleUrls` array as we did for `DashboardComponent`.
|
||||
|
||||
Here's the content for the aforementioned component CSS files.
|
||||
+makeTabs(
|
||||
`toh-5/ts/app/hero-detail.component.css,
|
||||
toh-5/ts/app/dashboard.component.css`,
|
||||
null,
|
||||
`app/hero-detail.component.css,
|
||||
app/dashboard.component.css`)
|
||||
:marked
|
||||
### Style the Navigation Links
|
||||
The designers gave us CSS to make the navigation links in our `AppComponent` look more like selectable buttons.
|
||||
We cooperated by surrounding those links in `<nav>` tags.
|
||||
|
||||
Add a `app.component.css` file to the `app` folder with the following content.
|
||||
+makeExample('toh-5/ts/app/app.component.css', 'css', 'app/app.component.css (Navigation Styles)')
|
||||
.l-sub-section
|
||||
:marked
|
||||
**The *router-link-active* class**
|
||||
|
||||
The Angular Router adds the `router-link-active` class to the HTML navigation element
|
||||
whose route matches the active route. All we have to do is define the style for it. Sweet!
|
||||
:marked
|
||||
Set the `AppComponent`’s `styleUrls` property to this CSS file.
|
||||
+makeExample('toh-5/ts/app/app.component.ts','style-urls', 'app/app.component.ts (styleUrls)')(format=".")
|
||||
:marked
|
||||
### Global application styles
|
||||
When we add styles to a component, we're keeping everything a component needs
|
||||
— HTML, the CSS, the code — together in one convenient place.
|
||||
It's pretty easy to package it all up and re-use the component somewhere else.
|
||||
|
||||
We can also create styles at the *application level* outside of any component.
|
||||
|
||||
Our designers provided some basic styles to apply to elements across the entire app.
|
||||
Add the following to a new file named `styles.css` in the root folder.
|
||||
+makeExample('toh-5/ts/styles.css', '', 'styles.css (App Styles)')(format=".")
|
||||
|
||||
:marked
|
||||
Reference this stylesheet within the `index.html` in the traditional manner.
|
||||
+makeExample('toh-5/ts/index.html','css', 'index.html (link ref)')(format=".")
|
||||
:marked
|
||||
Look at the app now. Our dashboard, heroes, and navigation links are styling!
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations")
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Application structure and code
|
||||
|
||||
Review the sample source code [in the live example for this chapter](/resources/live-examples/toh-5/ts/plnkr.html).
|
||||
Verify that we have the following structure:
|
||||
|
||||
.filetree
|
||||
.file angular2-tour-of-heroes
|
||||
.children
|
||||
.file node_modules
|
||||
.file app
|
||||
.children
|
||||
.file app.component.ts
|
||||
.file app.component.css
|
||||
.file dashboard.component.css
|
||||
.file dashboard.component.html
|
||||
.file dashboard.component.ts
|
||||
.file hero.ts
|
||||
.file hero-detail.component.css
|
||||
.file hero-detail.component.html
|
||||
.file hero-detail.component.ts
|
||||
.file hero.service.ts
|
||||
.file heroes.component.css
|
||||
.file heroes.component.html
|
||||
.file heroes.component.ts
|
||||
.file main.ts
|
||||
.file mock-heroes.ts
|
||||
.file index.html
|
||||
.file package.json
|
||||
.file styles.css
|
||||
.file tsconfig.json
|
||||
:marked
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Recap
|
||||
|
||||
### The Road Behind
|
||||
We travelled a great distance in this chapter
|
||||
- We added the Angular *Component Router* to navigate among different components.
|
||||
- We learned how to create router links to represent navigation menu items
|
||||
- We used router parameters to navigate to the details of user selected hero
|
||||
- We shared the `HeroService` among multiple components
|
||||
- We moved HTML and CSS out of the component file and into their own files.
|
||||
- We added the `uppercase` pipe to format data
|
||||
|
||||
### The Road Ahead
|
||||
We have much of the foundation we need to build an application.
|
||||
We're still missing a key piece: remote data access.
|
||||
|
||||
In a forthcoming tutorial chapter,
|
||||
we’ll replace our mock data with data retrieved from a server using http.
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
Loading…
Reference in New Issue