Angluar2のクイックスタートとチュートリアルを実施 - その6

前回の投稿[Angluar2のクイックスタートとチュートリアルを実施 - その5](http://qiita.com/gambare/items/fa1416cf2d2973cd270e)の続きです。
前回作成したソースを使用します。

本章ではルーティング(=画面遷移)を学びます。ナビゲーションとも呼びます。ビューからビューへの移動を定義します。

本章は結構長いです...

### 本投稿の参照元(英語)

[Routing - ts](https://angular.io/docs/ts/latest/tutorial/toh-pt5.html)

### 本章で学ぶこと

前述のほか、以下を学びます。

 - ダッシュボード[^dashboard]ビューを追加する
 - ルーターによりヒーロー一覧とダッシュボードビューを切り替える
 - `HeroService`を複数コンポーネントで共有する
 - htmlとcssを外部ファイルに移動する
 - パイプ`uppercase`の使い方を学ぶ

[^dashboard]: ダッシュボードとは「複数の情報源からデータを集め、概要をまとめて一覧表示する機能や画面、ソフトウェアのこと。」らしいです。[IT用語辞典 @e-words](http://e-words.jp/w/ダッシュボード.html)

ナビゲーションのイメージは以下です。(公式より拝借)

![イメージ](https://angular.io/resources/images/devguide/toh/nav-diagram.png)

### 実行結果

本章を完了すると以下リンク先のようなものが出来上がります。
尚、右上の青いボタンを押すことで全画面表示を行うことができます。
上図の実際の動きがわかります。
[live-examples](https://angular.io/resources/live-examples/toh-5/ts/plnkr.html)

### 事前準備

以下のコマンドによりサーバの起動を行います。プログラムの変更が即座にブラウザに反映されます。(サーバを落としていなければ不要です。)

```bash
cd angular2-tour-of-heroes
npm start
```

### 参考

公式の[Routing and Navigation](https://angular.io/docs/ts/latest/guide/router.html)(英語)では、本章についてのより詳細な情報が記載されています。

## 計画

以下の計画に沿って実施します

 - `AppComponent`の役割をナビゲーションのハンドルだけにする
 - ヒーロー一覧を`AppComponent`から`HeroesComponent`に移す
 - ルーティングを作成する
 - `DashboardComponent`を作成する
 - ダッシュボードにナビゲーション構造を持たせる

# `AppComponent`を分割

現在は`AppComponent`をロードするとヒーロー一覧を即座に表示しますが、画面の遷移を行うシェルにし、その他のことは他のコンポーネントにまかせます。
`AppComponent`クラスからヒーロー一覧を`HeroesComponent`に移動させましょう。



## `HeroesComponent`

`AppComponent`は既にヒーロー一覧用のプログラムなので、そのまま`HeroesComponent`に変更します。以下のリネームを実施します。

|種別|変更前|変更後|
|:--|:--|:--|
|ファイル名|`app/app.component.ts`|`app/heroes.component.ts`|
|クラス名| `AppComponent`|`HeroesComponent`|
|`@Component`デコレータの`selector`|`my-app`|`my-heroes`|


# 新しい`AppComponent`

新しい`app/app.component.ts`はアプリケーションシェルにします。いくつかのコンポーネントへのリンクを画面上部に表示し、ページへナビゲートします。まず以下を行います。

 - `app/app.component.ts`ファイルを作成する

 - `app/app.component.ts`でやること・・・
   - `AppComponent`クラスの定義
   - `@Component`デコレータと`my-app`セレクタの追加
   - `HeroesComponent`から`AppComponent`へ以下を移動させる
     - `title` クラスプロパティ
     - `@Component`のテンプレートの `<h1>` 要素。これは、`title`のバインドを含む
   - `@Component`のテンプレートに`<my-heroes>` 要素を追加し、ヒーロー一覧を表示
させる
   - `Component`のインポート
 - `app/app.module.ts`でやること・・・
   - `@AppModule`の`declarations`配列に`HeroesComponent`を追加し`<my-heroes>`タグを使用可能にする
   - `@AppModule`の`providers`に`HeroService`を追加
 - `app/heroes.component.ts`でやること・・・
   - `@Component`の`providers`を削除

結果`app/app.component.ts`, `app/app.module.ts`は以下の様になります。

```ts:app/app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <my-heroes></my-heroes>
  `
})
export class AppComponent {
  title = 'Tour of Heroes';
}
```

```ts:app/app.module.ts
import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';
import { AppComponent }        from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent }     from './heroes.component';
import { HeroService }         from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}
```

この段階でもまだアプリケーションは動作します。リファクタリングによる問題はおきません。

## ベースタグの追加

`index.html`の`<head>`セクションの下に`<base href="/">`タグを追加します。

```html:index.html
<head>
  <base href="/">
```

`base href`は必要です。理由は[Router#base-href](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)(英語)に記載があります。

# ルーティングの追加

今まで、自動的にヒーロー一覧を表示していた代わりに、ボタンをクリックしたら表示するように変更します。これには_コンポーネントルーター_を使用します。
Angularのルーターは外側におり、オプションモジュールで、`RouterModule`と呼ばれます。
ルーターは複数の提供サービス(`RouterModule`)と、複数のディレクティブ(`RouterOutlet`, `RouterLink`, `RouterLinkActive`)と、設定ファイル(`Routes`)の組み合わせからなります。最初に行うのは設定です。

## route[^route]の設定

[^route]: ルートと書くと、rootなのかrouteなのか分かりづらいのでアルファベットで記載します。root=根、route=道です。一応。

まだこのアプリは何のrouteも設定していません。コンフィグファイルを作りアプリケーションrouteを設定します。
_Routes_はユーザがボタンをクリックしたり、URLを貼り付けたときに、どのビューを表示するかをルーターに伝えます。
`app/app.routing.ts`ファイルを作成し、`HeroesComponent`へのrouteを定義します。

```ts:app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HeroesComponent }      from './heroes.component';

const appRoutes: Routes = [
  {
    path: 'heroes',
    component: HeroesComponent
  }
];
```

`Routes`はroute定義の配列です。現在routeは`HeroesComponet`への一つだけしかないですが後で追加します。_route_の定義は以下からなります。

 - path: routerがこのパスとブラウザのURLの一致を確認する(この場合`heroes`)
 - component: このrouteにナビゲートされたときルーターが作成するコンポーネント (この場合`HeroesComponent`)

`Routes`の詳細は、公式の[Routing](https://angular.io/docs/ts/latest/guide/router.html)(英語)にあります。

`routing`定数を宣言し、他で使用できるようにエクスポートします。この値は`RouterModule.forRoot`メソッドの戻り値です。このメソッドはroutesを引数に、<b>configured router module</b>を返します。`AppModule`の`@NgModule`にそれを追加します。

```ts:app/app.routing.ts
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
```

`forRoot`と呼ばれる理由は、アプリケーションroot(`AppModule`)用のルーターを提供するためです。`forRoot`メソッドはルーターサービスのプロバイダーとディレクティブを返します。これらはルーティングに使用します。

## 利用可能なルーターの作成
`app/app.routing.ts`に初期設定をしました。rootモジュールの`NgModule`にそれを追加しましょう。`routing`定数を`app.routing.ts`からインポートし、`AppModule `の`imports`配列に追加します。

```ts:app/app.module.ts
import { routing } from './app.routing';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    routing
  ],
})
export class AppModule {
}
```

## Router Outlet(ルーターの出口)

ブラウザのアドレスバーに`/heroes`を入れると`HeroesComponent`を表示しますが、ページのどこに表示するかは、テンプレートの`<router-outlet>`要素によって定めます。
`RouterOutlet`は`RouterModule`により提供されるディレクティブの一つです。
ルーターは各コンポーネントを`<router-outlet>`の場所に表示します。

## Router Links(ルーターリンク)

アプリ利用者により、route URLをアドレスバーに貼り付けられることを想定していません。テンプレートにアンカータグを追加することで、クリックされた際に`HeroesComponent`へナビゲートします。

```ts:app/app.component.ts
template: `
   <h1>{{title}}</h1>
   <a routerLink="/heroes">Heroes</a>
   <router-outlet></router-outlet>
 `
```

`routerLink`は、アンカータグにバインドされています。`RouterLink`ディレクティブはリンクをクリックしたとき、指定した文字列(ここでは`/heroes`)がどこへナビゲートするかをルーターへ伝えます。
直接のリンクをやめたため、ルートパスへルーティング指示をワンタイムバインディングとして定義します。route設定をもう一度確認してください。'/heroes'を`HeroesComponent`へ通しています。

公式の[Routing#link-parameters-array](https://angular.io/docs/ts/latest/guide/router.html#link-parameters-array)(英語)で、直接のルーターリンクや、リンクパラメータ配列について学べます。

ブラウザを更新すると、タイトルとheroesリンクだけになり、ヒーロー一覧が見えなくなります。
現在、ブラウザーのアドレスバーは`/`を指定しています。`HeroesComponent`を表示するのは`/heroes`なので何も見えません。後で対応します。アドレスを`/heroes`に変えれば、ヒーロー一覧が表示されます。

本項により、`AppComponent`は以下の様になりました。

```ts:app/app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
     <h1>{{title}}</h1>
     <a routerLink="/heroes">Heroes</a>
     <router-outlet></router-outlet>
   `
})
export class AppComponent {
  title = 'Tour of Heroes';
}
```

`AppComponent`は、ルーターとrouteされたビューが結びつけられました。このようなコンポーネントを_ルーターコンポーネント_と呼びます。

# ダッシュボード[^dashboard]の追加

ルーティングは複数のビューがあってこそ意味をなします。新しいビューを作ります。
`app/dashboard.component.ts`を以下の様に作成します。

```ts:app/dashboard.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'my-dashboard',
  template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent { }
```

後で役に立つように変更します。

## ダッシュボードのroute設定

ダッシュボードのビューが表示されるように、`app/app.routing.ts`に`Routes`に定義を追加します。

```ts:app/app.routing.ts
{
  path: 'dashboard',
  component: DashboardComponent
},
```

rootモジュールである`AppModule`クラス`NgModule`の`declarations`配列に、`DashboardComponent`を追加します。

```ts:app/app.module.ts
declarations: [
  AppComponent,
  DashboardComponent,
  HeroDetailComponent,
  HeroesComponent
],
```

## リダイレクトの設定

アプリケーションの開始時にアドレスバー`/dashboard`を表示し、ダッシュボードを表示します。これにはリダイレクトを使用します。
以下の様に`app/app.routing.ts`を変更します。

```ts:app/app.routing.ts
{
  path: '',
  redirectTo: '/dashboard',
  pathMatch: 'full'
},
```

リダイレクトについての詳細は、公式の[Router](https://angular.io/docs/ts/latest/guide/router.html#!#redirect)(英語)にあります。


## テンプレートにナビゲーションを追加

最後に、ヒーロー一覧より前に、ダッシュボードのリンクを追加します。

```ts:app/app.component.ts
template: `
   <h1>{{title}}</h1>
   <nav>
     <a routerLink="/dashboard">Dashboard</a>
     <a routerLink="/heroes">Heroes</a>
   </nav>
   <router-outlet></router-outlet>
```

リンクを`<nav>`タグ内に記載していますが、今は何も機能しません。後でスタイルを追加するときに便利なのでタグを記載します。

ブラウザで、アプリケーションroot(`/`)を表示するとダッシュボードが表示され、ダッシュボードとヒーロー一覧へのリンクが表示されます。

# ダッシュボードのトップヒーロー一覧

`dashboard.component.ts`のhtmlテンプレートを外部テンプレートファイルに変更します。以下の様に`template`の部分を`templateUrl`に変更してください。
ファイル名は`app/dashboard.component.html`とします。

```ts:app/dashboard.component.ts
templateUrl: 'app/dashboard.component.html',
```

なお、Angularでは相対パスはサポートしていませんので、アプリケーションルートからの絶対パスでURLを指定する必要があります。(今回の場合`app/dashboard.component.html`)
中身を以下の様に定義します。

```html:app/dashboard.component.html
<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>`を入れています。
また、`(clock)`にバインドしている`gotoDetail()`メソッドも後で追加します。

## ヒーローサービスの共有

ダッシューボードがヒーロー情報を取得するために`HeroService`を再利用します。
本章の序盤で`HeroService`を`HeroesComponent`のプロバイダから`AppModule`のプロバイダに移動しました。これにより、すべてのコンポーネントでシングルトンインスタンスの`HeroService`を使うことができます。

## ヒーローの取得

`app/dashboard.component.ts`のインポート記載部分を以下の様にします。

```ts:app/dashboard.component.ts
import { Component, OnInit } from '@angular/core';

import { Hero } from './hero';
import { HeroService } from './hero.service';
```

また、`DashboardComponent`を以下の様に実装します。


```ts:app/dashboard.component.ts
export class DashboardComponent implements OnInit {

  heroes: Hero[] = [];

  constructor(private heroService: HeroService) { }

  ngOnInit(): void {
    this.heroService.getHeroes()
      .then(heroes => this.heroes = heroes.slice(1, 5));
  }

  gotoDetail(hero: Hero): void { /* まだ実装しない */}
}
```

`HeroesComponent`と同じ様なものですが、ヒーロー一覧から4人(2番目〜5番目)を取得しているところと、スタブ`gotoDetail`を用意しているところが異なります。

ブラウザ上で4人のヒーローが表示されていることを確認できます。


# ヒーロー詳細への画面遷移

`HeroesComponent`の下に表示していたヒーロー詳細ですが、現在3つの方法で詳細を表示する必要があります。

 - ダッシュボードから選択したヒーロー
 - ヒーロー一覧から選択したヒーロー
 - URLから直接入力されたもの

ヒーロー詳細へのルートを個別の場所に表示する様に追加します。

## ヒーロー詳細へのroute

`app/app.routing.ts`に`HeroDetailComponent`へのrouteを追加します。
新しいrouteはちょっと普通と違う方法をとります。`HeroDetailComponent`を使用しますが、呼び出し元の`HeroesComponent`にも`DashboardComponent`にも呼び出す方法を持っていません。現在`HeroesComponent`が`hero`プロパティをセットする場合以下のようにバインドしています。

```html:
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
````

これは明白にほかのシナリオでは動きません。

## routeの調整

URLで、ヒーローの`id`を以下のように指定することで実現します。

```
/detail/11
```

`detail`は定数です。数字部分は変数で、このパラメータをヒーローの`id`とするルートが必要です。

## Routeの設定

以下の様にrouteを定義します。

```ts:app/app.routing.ts
{
  path: 'detail/:id',
  component: HeroDetailComponent
},
```
`:id`のコロンはプレースホルダーで`HeroDetailComponent`へ遷移させるときヒーローの`id`として使用します。

これで、アプリケーションのrouteの作成は完了です。
`'Hero Detail'`からのテンプレートへのリンクは作成しません。なぜなら、利用者は特定のヒーローへのナビゲーションはクリックしないからです。利用者は、ダッシュボードか、ヒーロー一覧をクリックします。
後の項で、ヒーローをクリックしたときの動きを取得します。

## `HeroDetailComponent`の修正

`HeroDetailComponent`を書き換えます。

```ts:app/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;
}
```

テンプレート部分は変更しません。大きな変更点はどうやってヒーローを取得するかの部分です。もう、親のコンポーネントからヒーロー情報を取得することをやめ、新しい`HeroDetailComponent`は`ActivatedRoute`サービスの`params`observable[^observable]からの`id`パラメータと`HeroService`の`id`から取得した`hero`を使います。

[^observable]: observableについては[次章](http://qiita.com/gambare/items/d35241a7d7fe7b7768ed#observables)で説明をします。

まず、必要なものをインポートします

```ts:app/hero-detail.component.ts
// まだ、Inputは残しますが, 後で消します。
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';

import { HeroService } from './hero.service';
```

コンストラクタに、privateな`ActivatedRoute`サービスと`HeroService`サービスを定義します。

```ts:app/hero-detail.component.ts
constructor(
  private heroService: HeroService,
  private route: ActivatedRoute) {
}
```

`HeroDetailComponent`クラスに`OnInit`インタフェースを実装します。

```
export class HeroDetailComponent implements OnInit {
```

`ngOnInit`ライフサイクルフック内で、`ActivatedRoute`サービスの`params`observableから`id`パラメータを取り出し`HeroService`で`id`からヒーローを取得します。

```ts:app/hero-detail.component.ts
ngOnInit(): void {
  this.route.params.forEach((params: Params) => {
    let id = +params['id'];
    this.heroService.getHero(id)
      .then(hero => this.hero = hero);
  });
}
```
`forEach`メソッドにより`id`を取得しています。`forEach`はrouteパラメータ配列を提供します。`id`は数値型(number)です。routeパラメータは通常、文字列型(string)です。Javascriptの(+)演算子により、routeパラメータを数値型に型変換しています。

## ヒーローサービスクラスへ`getHero()`メソッドの追加

`HeroService`に`getHero()`メソッドを追加します。`getHero()`メソッドでは、`getHeroes()`メソッドで取得したヒーロー一覧を`id`でフィルタします。

```ts:app/hero.service.ts
getHero(id: number): Promise<Hero> {
  return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
}
```

## 画面の戻りかた

複数の方法で`HeroDetailComponent`へ画面遷移できますが他への遷移はどうやるのでしょうか。
`AppComponent`から二つのリンクをクリックできます。また戻るボタンでも移動できます。第三の方法として`goBack`メソッドにより、ブラウザのヒストリースタックを使用して戻る方法[^back]があります。

[^back]: Javascriptのリファレンスを確認してください。[window.history.back()メソッド@アルファシス](http://alphasis.info/2013/06/javascript-historyobject-back/)

```ts:app/hero-detail.component.ts
goBack(): void {
  window.history.back();
}
```

アプリケーション外まで戻ることは、このチュートリアルでは許容していますが、実際のアプリでは抑止したいものです。[CanDeactivate guard](https://angular.io/docs/ts/latest/api/router/index/CanDeactivate-interface.html)(英語)で方法が学べるはずです。
`goBack()`メソッドをコンポーネントのテンプレートに以下の様に追加し、バインドします。

```ts:
<button (click)="goBack()">Back</button>
```

この追加したボタンと一緒に、ヒーロー詳細を表示するテンプレートを別ファイル`app/hero-detail.component.html`に移します。

```html:app/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>
```

`app/hero-detail.component.ts`側はテンプレートからテンプレートURLに変更し、htmlファイルを読み込みます。
```app/hero-detail.component.ts
templateUrl: 'app/hero-detail.component.html',
```

ブラウザを更新し、結果を確認してください。

# ダッシュボードからヒーローを選択する

ダッシュボードからヒーローが選択されたとき、ヒーロー詳細を表示/編集可能にするべきです。ダッシュボードのテンプレートでは各ヒーローをクリックすると`gotoDetail()`メソッドを呼び出します。

```html:app/dashboard.component.html
<div *ngFor="let hero of heroes" (click)="gotoDetail(hero)" class="col-1-4">
```

スタブ化していた`gotoDetail()`を今、実装します。

```ts:app/dashboard.component.ts
gotoDetail(hero: Hero): void {
  let link = ['/detail', hero.id];
  this.router.navigate(link);
}
```

`gotoDetail`メソッドは以下の２ステップにより画面遷移します。

 1. _link_配列にrouteパラメータを格納
 2. `router`の`navigate`メソッドにそれを渡す

画面遷移を行うために以前`AppComponent`のテンプレートにルーターリンクを実装しました。これらのリンクは一つの要素のみ持ちますが、今回のパラメータ配列は２つの要素を持ちます。目的のrouteの<b>_path_</b>とヒーローの`id`をセットする<b>_route parameter_</b>です。
この二つのアイテム<b>_path_</b>と<b>_:id_</b>は以前、`app.routing.ts`にパラメータ化しています。

```ts:app/app.routing.ts
{
  path: 'detail/:id',
  component: HeroDetailComponent
},
```

`DashboardComponent`はまだルーターを持っていません。インポートしてコンストラクタに登録します。

```ts:
import { Router } from '@angular/router';
```
```ts:
constructor(
  private router: Router,
  private heroService: HeroService) {
}
```
ブラウザを更新しダッシュボードからヒーローを選択すると、ヒーロー詳細に画面遷移します


# `HeroesComponent`からのヒーローの選択

`HeroesComponent`にも同じ様に実装します。
このコンポーネントは現在、"マスター/ディティール"の形式(一覧と詳細)をとっています。

```ts:app/heroes.component.ts
template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <my-hero-detail [hero]="selectedHero"></my-hero-detail>
`,
```
`<h1>`タグを削除します。`AppComponent`から`HeroesComponent`に変更したときのごみです。次に`<my-hero-detail>`タグも削除します。もうここにはヒーロー詳細を表示しません。ヒーロー詳細は専用のページを用意し、ダッシュボードと同様、そのページへのrouteを作ります。
ここでちょっとひねった方法をとります。ヒーロー一覧からヒーローを選択しても、すぐにはヒーロー詳細を表示せずに、まずはミニ詳細を代わりに表示し、ミニ詳細に表示されたボタンをクリックすることで、フルの詳細ページに移動するようにします。

## ミニ詳細の追加

`<my-hero-detail>`の場所に以下のhtmlの一部を追加します。

```html:
<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>
```

ヒーロークリックにより、以下の様なものがヒーロー一覧の下に表示されます。

![イメージ](https://angular.io/resources/images/devguide/toh/mini-hero-detail.png)

## パイプによる大文字変換

パイプ演算子(`|`)と`uppercase`パイプにより表示文字を大文字に変更できます。

```ts:
{{selectedHero.name | uppercase}} is my hero
```

パイプは「文字列のフォーマット」「通貨単位」「日付」などを表示するには、いい方法です。Angularにはいくつかのパイプと書き方が用意されています。
詳細は公式の[Pipes](https://angular.io/docs/ts/latest/guide/pipes.html)(英語)に記載されています。

## コンポーネント外に内容を移す

まだミニ詳細の[_View Details_]ボタンを押したときに`HeroDetailComponent`へ画面遷移する機能に対応していませんが、ヒーロー一覧コンポーネントは非常に大きく、ほとんどはcss部分です。htmlとcssを他のファイルに移動します。
以下のマイグレーションを行います。

 1. htmlテンプレート部分を`app/heroes.component.html`にカット&ペースト
 2. スタイル部分を`heroes.component.css`にカット&ペースト
 3. `@Component`デコレータに`templateUrl`と`styleUrls`プロパティを追加し、それぞれのファイルパスを記載

`styleUrls`プロパティは、スタイルシートのファイルパスの配列です。必要に応じ複数のスタイルを適用できます。

```ts:app/heroes.component.ts
@Component({
  selector: 'my-heroes',
  templateUrl: 'app/heroes.component.html',
  styleUrls:  ['app/heroes.component.css']
})
```

ダッシュボードと同じ様にコンポーネントクラスを更新します

 1. `router`をインポート
 2. コンストラクタで`router`をインジェクト
 3. `router.navigate`メソッドに呼ばれる`gotoDetail`メソッドの実装

<!-- 2パートのヒーロー詳細リンクパラメータ配列を含む？ -->

`app/heroes.component.ts`は以下の様に修正されます。

```ts:app/heroes.component.ts
export class HeroesComponent implements OnInit {
  heroes: Hero[];
  selectedHero: Hero;

  constructor(
    private router: Router,
    private heroService: HeroService) { }

  getHeroes(): void {
    this.heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

  ngOnInit(): void {
    this.getHeroes();
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  gotoDetail(): void {
    this.router.navigate(['/detail', this.selectedHero.id]);
  }
}
```

ブラウザを更新しクリックするとナビゲートできるようになります。ダッシュボードから、詳細へいったり、ダッシュボードに戻ったり、ヒーロー一覧から、ミニ詳細を通して、ヒーロー詳細を表示したり、ヒーロー一覧へ戻ったりができます。
本章の機能の実現は以上です。

# アプリケーションのスタイル

機能は実現できましたが、見た目がイマイチです。cssを変更します。

## ダッシュボードのスタイル

`app/dashboard.component.ts`の`@Component`デコレータに以下を追加します。

```ts:app/dashboard.component.ts
styleUrls: ['app/dashboard.component.css']
```

## 洒落たヒーロー詳細

`DashboardComponent`から`hero`プロパティ、`@Input`デコレータとそれらをインポートしているインポート句を削除し、以下のcssファイルを追加します。

```css:app/hero-detail.component.css
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;
}
button:disabled {
  background-color: #eee;
  color: #ccc; 
  cursor: auto;
}
```

```css:app/dashboard.component.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;
}
.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;
    }
}
```

## ナビゲーションリンクのスタイル
`AppComponent`の`<nav>`タグにもスタイルを適用します。
`app/app.component.css`を追加します。

```css:app/app.component.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.active {
  color: #039be5;
}
```


### _routerLinkActive_ディレクティブ

Angularでは、`routerLinkActive`ディレクティブが提供されています。htmlナビゲーション要素に追加することでrouteがアクティブなrouteになったときのcssが定義できます。

```html:app/app.component.ts
template: `
  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
    <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
`,
```

`AppComponent`も`styles`プロパティから`styleUrls`プロパティに変更します。

```ts:app/app.component.ts
styleUrls: ['app/app.component.css'],
```

## グローバルアプリケーションスタイル

アプリケーションの外へスタイルを作成できます。
ベーシックなスタイルをアプリに適用する場合、[クイックスタート](http://qiita.com/gambare/items/e52b49c09890e1180610)の[スタイルシートの追加](http://qiita.com/gambare/items/e52b49c09890e1180610#スタイルシートの追加)で紹介した方法を用います。

```css:styles.css_抜粋
/* Master Styles */
h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
h2, h3 {
  color: #444;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: lighter;
}
body {
  margin: 2em;
}
body, input[text], button {
  color: #888;
  font-family: Cambria, Georgia;
}
/* . . . */
/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}
```

スタイルシートの全文は公式の[こちら](https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/styles.css)です。
必要に応じて`index.html`をスタイルシートを参照する様に変更します。

```html:index.html
<link rel="stylesheet" href="styles.css">
```

現在の見た目は以下の様になります。

![イメージ](https://angular.io/resources/images/devguide/toh/dashboard-top-heroes.png)

---

本章は以上です。まだこのアプリでは、リモートデータアクセスを実装していません。[次章](http://qiita.com/gambare/items/d35241a7d7fe7b7768ed)ではモックデータを置換し、httpを使ってデータを取得する方法の説明を行います。

---