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

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

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

(バージョンが異なりますが、大きな影響はないと思います。)
[Master/Detail - ts](https://angular.io/docs/ts/latest/tutorial/toh-pt2.html)

### 本章で学ぶこと

以下を学習します。

 - 選択可能なヒーローの一覧を表示する
 - 一覧選択時(クリック時)ヒーローの詳細を表示する
 - ディレクティブ`ngFor`と`ngIf`の使い方


### 実行結果

本章を完了すると以下リンク先のようなものが出来上がります。
ヒーローのリストを表示し、選択(クリック)すると、下に詳細が表示されます。
[live-examples](https://angular.io/resources/live-examples/toh-2/ts/plnkr.html)

### 事前準備

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

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

#ヒーローの一覧表示

## ヒーローの作成

ヒーロークラスの定数配列`HEROES`を追加します。
(ヒーロークラスは、`id`,`name`のプロパティを持ちます。)
※データはサービスから取得すべきですが、あくまでもモックなのでソースに直書きします。後の章で変更します。

```ts:app/app.component.ts
const 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' }
];
```

## ヒーロー配列の公開
`AppComponent`クラスのプロパティを、先ほど定義した定数配列`HEROES`を使用するため、以下の通り変更します。

```ts:app/app.component.tsのAppComponentクラス
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
}
```

## ngForを使用したヒーロー表示
ヒーロー名`Windstorm`のみを表示する状態から、ヒーローの一覧を表示するために、`app.component.ts`のhtmlテンプレート部分を修正します。

```html:app/app.component.tsの@Componentデコレータのtemplate
  <h1>{{title}}</h1>
  <ul class="heroes">
    <li *ngFor="let hero of heroes">
     <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
```

`heroes`配列をhtmlテンプレートに表示したい場合、テンプレートで`*ngFor`を使用します。
`let hero of heroes`により、`heroes`配列の要素数分、ローカル変数`hero`に値が格納され、`<li>`タグ及び内包のタグが動的に生成されます。

> `ngFor`のアスタリスク`*`は必要です。
> `ngFor`については以下を参照願います。
> [Displaying Data](https://angular.io/docs/ts/latest/guide/displaying-data.html#ngFor)(英語)
> [Template Syntax](https://angular.io/docs/ts/latest/guide/template-syntax.html#ngFor)(英語)

これにより、ブラウザ上にヒーローの一覧がリスト表示されます。(表示されない場合更新してください。)

## スタイルの追加

htmlテンプレートのスタイルは、cssのほか、`@Component`デコレータ内に`Styles`属性を追加することでも変更できます。htmlとcssを1対1で定義しているため、cssの内容が他のhtmlに影響を与えることはありません。
ここでは、マウスカーソルがホーバーしたときとクリックしたときに見た目を変更する定義を行っています。

cssについての一般的な知識は以下を参照ください。
[いまさら聞けないCSS／スタイルシート入門（1）@IT](http://www.atmarkit.co.jp/ait/articles/0809/11/news154_2.html)

```css:app/app.component.tsの@Componentデコレータ

styles: [`
  .selected {
    background-color: #CFD8DC !important;
    color: white;
  }
  .heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
  }
  .heroes li {
    cursor: pointer;
    position: relative;
    left: 0;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
  }
  .heroes li.selected:hover {
    background-color: #BBD8DC !important;
    color: white;
  }
  .heroes li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
  }
  .heroes .text {
    position: relative;
    top: -3px;
  }
  .heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color: #607D8B;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
  }
`]
```

# ヒーローを選択する

「マスター/ディテール」のUIパターンにより、マスター(ヒーローの一覧)からディテール(ヒーローの詳細)を選択できるようにします。

## クリックイベント
`<li>`タグに`(click)`を追加し、クリックイベントをバインドします。(Javascript標準の`.onClick`ではありません。)

```html:app/app.component.tsの@Componentデコレータ内template
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
  <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
```

これにより`<li>`タグ内のクリック時に、後ほど`AppComponent`クラスで定義する`onSelect()`メソッドが呼ばれます。このとき選択した`Hero`クラスが`onSelect`メソッドへの引数になります。この`hero`は、`ngFor`でイテレートされた`heroes`配列の要素です。

## クリックハンドラ(`onSelect()`メソッド)の追加

選択したヒーローを取得するために、`AppComponent`クラスに`onSelect()`メソッドを追加します。このメソッドは、ユーザがクリックしたヒーローを`AppComponent`クラスの`selectedHero`プロパティとして取得します。
`AppComponent`クラスを以下の通り変更します。

```ts:app/app.component.tsのAppComponentクラス
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;

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

次に、以前作成したテキストボックスに、`selectedHero`プロパティの値を出力します。
`onSelect()`メソッドで取得した`seletedHero`プロパティをhtmlテンプレートで使用します。

```html:app/app.component.tsの@Componentデコレータ内テンプレート

<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
  <label>name: </label>
  <input [(ngModel)]="selectedHero.name" placeholder="name"/>
</div>
```

## 未選択時のヒーロー詳細を隠す
ヒーローを選択していない場合、テキストボックスを表示しないようにします。これは、`selectedHero === undefined`の場合です。
`ngIf`ディレクティブによる条件判定により、それを実現します。

```html:app/app.component.tsの@Componentデコレータ内テンプレート
<h1>{{title}}</h1>
<ul class="heroes">
    <li *ngFor="let hero of heroes">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <div *ngIf="selectedHero">
    <h2>{{selectedHero.name}} details!</h2>
    <div><label>id: </label>{{selectedHero.id}}</div>
    <div>
      <label>name: </label>
      <input [(ngModel)]="selectedHero.name" placeholder="name"/>
    </div>
  </div>

```

<b>`ngIf`の前のアスタリスク(*)は必須です。</b>

`ngFor`や`ngIf`はストラクチュアルディレクティブ(構造上のディレクティブ structural directives)と呼ばれます。これはDOMの一部の構造を変更するためです。
その他ストラクチュアルディレクティブについては以下を参照してください。
[Structural Directives](https://angular.io/docs/ts/latest/guide/structural-directives.html)(英語)
[Template Syntax](https://angular.io/docs/ts/latest/guide/template-syntax.html#directives)(英語)

本項により選択したヒーローの詳細情報が、ヒーローの一覧の下に表示されるようになります。

## 選択したヒーローのスタイル

選択したヒーローを分かりやすくするためにスタイル(見た目)を変更したい場合、先ほどstylesに追加したクラスセレクタ`.selected`を使用するため、ヒーローの一覧の`<li>`タグに<b>プロパティバインディング</b>`class.selected`を追加します。

```ts
[class.selected]="hero === selectedHero"
```

これはイテレータにより取得する変数`hero`と現在選択中の`selectedHero`が一致する場合CSSのクラスセレクタ `.selected`を適用し、一致しない場合適用を解除します。

Angular2のプロパティバインディングについては以下を参照ください。
[property bindings](https://angular.io/docs/ts/latest/guide/template-syntax.html#property-binding)(英語)

本章により、app/app.component.tsは以下のようになります。

```ts:app/app.component.ts
import { Component } from '@angular/core';
export class Hero {
  id: number;
  name: string;
}
const 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' }
];
@Component({
  selector: 'my-app',
  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>
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
  `,
  styles: [`
    .selected {
      background-color: #CFD8DC !important;
      color: white;
    }
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li.selected:hover {
      background-color: #BBD8DC !important;
      color: white;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

```


-------------
本章は以上です。[次章](http://qiita.com/gambare/items/bd14e8f36cc40194c5dd)では複数のコンポーネント(部品)を使用する方法を説明します。