Angulra2 View建立 with VS2015 - Step6. Router_part1
在 Router 章節前半部我們已經將基本功能實做出來,現在則是針對需求來做額外功能
雖然我們在 HeroesComponent 組件顯示了所選英雄的詳細資料,但是我們還沒有指向到 HeroDetailComponent 組見過,所以我們需求如下:
- 從儀錶板(Dashboard) 指向到選定的英雄。
- 從英雄列表(Heroes) 指向到選定的英雄。
- 把一個指向該英雄的連結複製到瀏覽器的網址列。
Routing to a hero detail
新路由特別的地方在於我們必須告訴 HeroDetailComponent 該顯示哪個英雄。
父組件 HeroesComponent 透過綁定的方式來把一個英雄設為組件的 hero 屬性:
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
我們可以把英雄的 id 加到 URL 中,當指向 id 為 11 的英雄時,我們預期的 URL 如下:
/detail/11
URL 中的 /detail/ 部分是固定不變的,但是結尾數字 id 部分會隨著選取目標的不同而改變。
因此我們把這部分表示成一個參數(parameter)或 token ,以便能取得英雄的 id。
打開 app.router.ts 檔案,定義新的路由設定:
//記得要事先 import
import { HeroDetailComponent } from './hero-detail.component';
{
path: 'detail/:id',
component: HeroDetailComponent
},
路徑中的冒號( : )表示 :id 是一個 placeholder ,當指向這個 HeroDetailComponent 組件時,它會被填入某個特定英雄的 id。
修改 HeroDetailComponent
樣板的部分可以不用更改,我們會用原來的方式來顯示英雄資料。需要修改的原因在於如何取得英雄資料。
我們不會在從父組件的屬性綁定中取得資料。新的 HeroDetailComponent 應該從 ActivatedRoute 服務中取得 params 中的 id 參數,並透過 HeroService 服務得到指定 id 的英雄資料。
首先導入 ActivatedRoute 和 HeroService
import { ActivatedRoute } from '@angular/router';
import { HeroService } from './hero.service';
接著導入 Onlnit 和 OnDestory 介面,因為我們需要在 ngOnlnit 生命週期內呼叫 HeroService ,並且在 ngOnDestory 中清除對 parpms 的訂閱。
import { Component, OnInit, OnDestroy } from '@angular/core';
告訴這個類別,我們要實作 OnInit 和 OnDestroy 介面。
export class HeroDetailComponent implements OnInit, OnDestroy {
在 ngOnInit 生命週期中,從 RouterParams 服務中取得 id 參數,並使用 HeroService 來得到此 id 的英雄資料。
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.heroService.getHero(id)
.then(hero => this.hero = hero);
});
}
在 ngOnDestroy 生命週期中,我們取消了對 params 的訂閱。
ngOnDestroy() {
this.sub.unsubscribe();
}
注意我們是如何呼叫 subscribe 方法,這將實現我們從 router 中擷取 id。
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.heroService.getHero(id)
.then(hero => this.hero = hero);
});
英雄的 id 型態是 numbe,而路由參數的值是 string,所以我們需要透過 JavaScript 的 ( + ) 來把參數的字串轉為數字。
接著我們發現 HeroService 中沒有一個叫 getHero() 的方法,所以增加一個方法然後透過 id 的方式來取得資料。
getHero(id: number) {
return this.getHeroes()
.then(heroes => heroes.find(hero => hero.id === id));
}
再來增加一個回到上一層的方法,goBack()。
goBack() {
window.history.back();
}
接著把 goBack 方法綁定到樣板中。
<button (click)="goBack()">Back</button>
接著可以把樣板部分獨立到 hero-detail.component.html 中。
<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>
<button (click)="goBack()">Back</button>
</div>
接著更新組件的中繼資料,用一個 templateUrl 來指向剛剛所建立的樣板檔案。
templateUrl: 'app/hero-detail.component.html',
以下是 HeroDetailComponent 完整程式;
// 原始 hero-detail.component.ts
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'my-hero-detail',
template: `
<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>
</div>
`
})
export class HeroDetailComponent {
@Input()
hero: Hero;
}
// 修改後如下
import { Hero } from './hero';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { HeroService } from './hero.service';
@Component({
selector: 'my-hero-detail',
templateUrl: 'hero-detail.component.html',
})
export class HeroDetailComponent implements OnInit, OnDestroy {
hero: Hero;
sub: any;
constructor(
private heroService: HeroService,
private route: ActivatedRoute) {
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.heroService.getHero(id)
.then(hero => this.hero = hero);
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
goBack() {
window.history.back();
}
}