Angulra2 View建立 with VS2015 - Step6. Router
切割 AppComponent
原本的應用會直接讀取 AppComponent 並顯示英雄列表。
修改後的應用將會多一層包裝(shell),它會選擇儀表板和英雄列表(Dashboard and Heroes)。
所以 AppComponent 應該只負責處理路徑(router)問題,接著把英雄列表的顯示功能,從 AppComponent 轉移到 HeroesComponent 中。
HeroesComponent
AppComponent 的工作已經轉移到 HeroesComponent 了,所以直接將它改名為 HeroesComponent ,接著重新建立新的 AppComponent。
更改步驟如下:
- 把 app.component.ts 檔案(File)名稱改為 heroes.component.ts
- 把 AppComponent 類別(Class)名稱改為 HeroesComponent
- 把 my-app 選擇器(Selector)名稱改為 my-heroes
@Component({
selector: 'my-heroes',
})
export class HeroesComponent implements OnInit {
}
重新建立 AppComponent
新的 AppComponent 會變成應用程式的外包裝(shell),接著把我們要指向的路徑放到下面的步驟中。
初始化步驟如下:
- 在 /app 底下新增一個 app.component.ts 檔案。
- 定義一個 AppComponent 的類別並 export 它,以便在 main.ts 啟動時能引用。
- export 應用程式的 title 屬性。
- 在類別上方增加 @Component 中繼資料裝飾器(metadata decorator),其中包含 my-app 選擇器(selector)。
- 在樣板(template)中增加一個 <h1> 標籤,接著綁定 title 屬性。
- 增加 HeroesComponent 組件到 directives 陣列中,以便 Angular 可以認識 <my-heroes> 標籤。
- 增加 HeroesService 到 Providers 陣列中,因為每一個頁面都會需要用到它。
- import 所需要引用的東西。
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<my-heroes></my-heroes>
`,
directives: [HeroesComponent],
providers: [
HeroService
]
})
export class AppComponent {
title = 'Tour of Heroes';
}
因為我們不希望同一組服務存在於兩個不同層級的應用程式中。
目前為止我們將 AppComponent 重構成一個新的 AppComponent 和 HeroesComponent。
增加Router
打開 index.html 並在 <head> 最上層的區域增加 <base href="/"> 語法。
應用程式還沒設定路由,因此在 app/ 底下建立一個 router.ts 檔案,這個檔案要做兩件事情:
- 設定路由
- 把這個路由導出(export)並增加到啟動程式(bootstrap)中
路由的工作是告訴路由器,當使用者點擊連結或者輸入網址時應該顯示哪個頁面。
接著開始設定第一個路由,連接到 HeroComponent
import { provideRouter, RouterConfig } from '@angular/router';
import { HeroesComponent } from './heroes.component';
const routes: RouterConfig = [
{
path: 'heroes',
component: HeroesComponent
}
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
這個 RouteConfig 是一個定義路由的陣列資料,目前先只定義一組路由,後續會再額外增加。
RouteConfig 主要結構包含兩個部分:
- path:路由器會用它來比對,路由器所設定的路徑和網址列中當下的路徑,例:/heroes 。
- component:當指向這個路由時,路由器需要建立的組件,例:HeroesComponent。
讓路由器生效
組件路由器(Component Router)本身是一個服務。因此我們需要導入 APP_ROUTER_PROVIDERS (它包含了我們設定好的路由器),使用 import 的方式把它加進 main.ts 中。
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS
]);
Router Outlet
如果我們把路徑 /heroes 複製到網址列中,路由器會指向 'Heroes' 路由,並顯示 HeroesComponent 組件。但問題是該把它顯示在哪?
所以我們必須告訴它位置,所以我們把 <router-outlet> 標籤加進樣板(template)的下方。
RouterOutlet
is one of the ROUTER_DIRECTIVES
當指向某個應用程式時,路由器就把組件顯示在 <router-outlet> 裡面。
路由器連接
我們當然不會真的讓使用者在網址列中輸入網址。而是在樣板上增加一個 <a> 的標籤,點擊後就會指向 HeroesComponent 組件。修改過的 app.component.ts 如下:
template: `
<h1>{{title}}</h1>
<a [routerLink]="['/heroes']">Heroes</a>
<router-outlet></router-outlet>
`,
刷新頁面後會發現到只剩原有的標題,而英雄列表則不見了,可以修改網址路徑為 /heroes 或點擊 heroes 連接。
在這個階段,AppComponent 程式碼如下:
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
import { HeroService } from './hero.service';
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<a [routerLink]="['/heroes']">Heroes</a>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
providers: [
HeroService
]
})
export class AppComponent {
title = 'Tour of Heroes';
}
現在 AppComponent 上已經有了路由器,並能顯示指向的頁面。所以我們打算將這類的路由器組件從中分離出來。
增加一個儀錶板(Dashboard)
當我們有多個頁面時,路由才有意義。所以我們需要增加另一個頁面。
先建立一個基礎結構的 dashboard.component.ts ,後續會再增加實作內容。
import { Component } from '@angular/core';
@Component({
selector: 'my-dashboard',
template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent { }
設定儀錶板路由
打開 app.router.ts 檔案,首先導入 DashboardComponent 類別,這樣就可以引用它了。
然後把 'Dashboard' 路由的定義加到 @RouteConfig 陣列中。
{
path: 'dashboard',
component: DashboardComponent
},
如果希望啟動時有預設路徑,如:/dashboard ,或者路徑為空時導向預設路徑。我們可以增加以下方法來重新指向。
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full'
},
接著在 app.component.ts 的樣板上增加一個儀表板的連接功能。
目前在 <nav> 標籤中放了兩個連接,後續會加上一些樣式,以利辨識。
template: `
<h1>{{title}}</h1>
<nav>
<a [routerLink]="['/dashboard']" routerLinkActive="active">Dashboard</a>
<a [routerLink]="['/heroes']" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
為了讓儀錶板變的簡單明瞭,我們從 dashboard.component.ts 中更改了樣板。
templateUrl: 'app/dashboard.component.html',
接著我們建立 dashboard.component.htnl 檔案
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<div *ngFor="let hero of heroes" (click)="gotoDetail(hero)" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</div>
</div>
這裡又使用了 *ngFor 來顯示英雄列表,還額外增加了一個 <div> 標籤來做美化。
其中的 (click) 綁定到了 gotoDetail 方法,接下來會開始調整這些方法。
共用 HeroService
在前面的章節中我們從 HeroesComponent 的 providers 陣列中移除了 HeroService ,並把它移到 AppComponent 的 providers 中。
所以在 DashboardComponent 組件中,Angular 會把它注入進來,我們就可以使用它了。
打開 dashboard.component.ts 檔案,把必要的 import 語法加進去。
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
接著我們必須實作 Onlint 介面,因為我們要在 Onlint 方法中初始化英雄資料。
需要導入 Hero 類別和 HeroService 類別來引用他們的資料,實作 DashboardComponent 類別如下
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'my-dashboard',
templateUrl: 'dashboard.component.html',
})
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 */ }
}