3. フィーチャーコンポーネントの作成　Angular チュートリアル

# 参考

https://angular.jp/tutorial/toh-pt3

# 前段

現時点では、 `HeroesComponent` はヒーローのリストと選択されたヒーローの詳細の両方を表示している。

1 つのコンポーネントにすべての機能を保持しておくと、アプリケーションが成長するにつれて維持できなくなる。
大きなコンポーネントを、特定のタスクやワークフローに焦点を当てた小さなサブコンポーネントに分割したいと思うだろう。

このページでは、ヒーローの詳細を別の再利用可能な `HeroDetailComponent` に移動させることで、その道への第一歩を踏み出す。

`HeroesComponent` はヒーローのリストのみを表示する。
`HeroDetailComponent` は選択されたヒーローの詳細を表示する。

# HeroDetailComponent を作成する

Angular CLI を使用して、 `hero-detail` という名前の新しいコンポーネントを生成する。

```:Terminal
ng generate component hero-detail
```

このコマンドは `HeroDetailComponent` ファイルの雛形を生成して、 `AppModule` でこのコンポーネントを宣言する。
このコマンドは次の雛形を生成する。

* `src/app/hero-detail` ディレクトリの作成

このディレクトリの中に 4 つのファイルを生成する。

* コンポーネントスタイルのための CSS ファイル
* コンポーネントテンプレートのための HTML ファイル
* `HeroDetailComponent` と名付けられたコンポーネントクラスの TypeScript ファイル
* `HeroDetailComponent` クラスのテストファイル

また、このコマンドは `src/app/app.module.ts` ファイルの `@NgModule` デコレーター中に `declarations` として `HeroDetailComponent` を追加する。

## template を記述する

ヒーローの詳細の HTML を `HeroesComponent` テンプレートの下部から切り取り、 `HeroDetailComponent` テンプレートに生成されたボイラープレートへ貼り付ける。

貼り付けられた HTML は `selectedHero` を参照しちえる。
新しい `HeroDetailComponent` は、選択されたヒーローだけでなく、 *どんな* ヒーローも表示することができる。
なので、テンプレート内すべての "selectedHero" を "hero" に置き換えよう。

完了したら、 `HeroDetailComponent` テンプレートは次のようになる。

```html:src/app/hero-detail/hero-detail.component.html
<div *ngIf="hero">

  <h2>{{hero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label for="hero-name">Hero name: </label>
    <input id="hero-name" [(ngModel)]="hero.name" placeholder="name">
  </div>

</div>
```

## @[Input](https://angular.jp/api/core/Input) () hero プロパティを追加する

`HeroDetailComponent` テンプレートは、 `Hero` 型であるコンポーネントの `hero` プロパティにバインドされている。

`HeroDetailComponent` クラスのファイルを開いて、 `Hero` シンボルをインポートする。

```typescript:src/app/hero-detail/hero-detail.component.ts
import { Hero } from '../hero';
```

`hero` プロパティは `@Input()` デコレーターで注釈された [*Input* プロパティでなければならない](https://angular.jp/guide/inputs-outputs) 。
これは、 *外部の* `HeroesComponent` がこのようにバインドするため。

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

`Input` シンボルを含めるために、 `@angular/core` の import 文を修正しよう。

```typescript:src/app/hero-detail/hero-detail.components.ts
import { Component, OnInit, Input } from '@angular/core';
```

`@Input()` デコレーターが前についた `hero` プロパティを追加する。

```typescript:src/app/hero-detail/hero-detail.components.ts
@Input() hero?: Hero;
```

これが `HeroDetailComponent` クラスに行うべき唯一の変更。
これ以上のプロパティも、表示するためのロジックも必要ない。
このコンポーネントは `hero` プロパティを通して hero オブジェクトを受け取り、それを表示するだけ。

# HeroDetailComponent を表示する

ヒーローの詳細は、テンプレートのその部分を削除する前は `HeroesComponent` 自身が表示していた。
このセクションでは、ロジックを `HeroDetailComponent` に委譲する方法を説明する。

2 つのコンポーネントには親子関係がある。
ユーザーがリストからヒーローを選択するたびに新しいヒーローを表示するため、親の `HeroesComponent` はそれを送ることで、子の `HeroDetailComponent` を制御する。

`HeroesComponent` *class* は変更せず、 *template* を変更する。

## HeroesComponent テンプレートを更新する

`HeroDetailComponent` のセレクターは、 `'app-hero-detail'` 。

ヒーローの詳細ビューがかつて存在した `HeroesComponent` テンプレートの下部に `<app-hero-detail>` 要素を追加しよう。

次のように `HeroesComponent.selectedHero` を、この要素の `hero` プロパティにバインドさせる。

```html:heroes.component.html
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
```

`[hero]="selectedHero"` は、 Angular の [プロパティバインディング](https://angular.jp/guide/property-binding) 。

これは `HeroesComponent` の `selectedHero` プロパティから、ターゲット要素の `hero` プロパティへの単方向データバインディング。
ここでは、 `HeroDetailComponent` の `hero` プロパティがマッピングされている。

ユーザーがリスト内のヒーローをクリックすると、 `selectedHero` が変更される。
`selectedHero` が変更されると、 *property binding* は `hero` を更新して、 `HeroDetailComponent` は新しいヒーローを表示する。

修正された `HeroesComponent` テンプレートはこのようになる。

```html:heroes.component.html
<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>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>
```

ブラウザがリフレッシュされると、以前と同じようにアプリケーションが再び動き始める。

# 何が変わったのか？

[以前](https://angular.jp/tutorial/toh-pt2) は、ユーザーがヒーロー名をクリックするたびに、ヒーローのリストの下にヒーローの詳細が表示されていた。
今では `HeroDetailComponent` が `HeroesComponent` の代わりにそれらの詳細を表示している。

元の `HeroesComponent` を 2 つのコンポーネントにリファクタリングすることで、現在も将来も利益が得られる。

1. `HeroesComponent` の責任を減らした。
1. 親の `HeroesComponent` に触れることなく、 `HeroDetailComponent` をリッチなヒーローエディタに進化させることができる。
1. ヒーローの詳細ビューに触れることなく、 `HeroesComponent` を進化させることができる。
1. 将来のコンポーネントのテンプレートで、 `HeroDetailComponent` を再利用することができる。

# 最終的なコードレビュー

このページで解説したコードファイルは次の通り。

```typescript:src/app/hero-detail/hero-detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
  @Input() hero?: Hero;

  constructor() { }

  ngOnInit() {
  }

}
```

```html:src/app/hero-detail/hero-detail.component.html
<div *ngIf="hero">

  <h2>{{hero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label for="hero-name">Hero name: </label>
    <input id="hero-name" [(ngModel)]="hero.name" placeholder="name">
  </div>

</div>
```

```html:src/app/heroes/heroes.component.html
<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>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>
```

```typescript:src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    HeroDetailComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

# まとめ

* 独立し、再利用可能な `HeroDetailComponent` を作成した。
* 親の `HeroesComponent` が子の `HeroDetailComponent` を制御できるように、プロパティバインディングを使用した。
* 外部の `HeroesComponent` によるバインディングが `hero` プロパティを利用できるように `@Input` デコレータを使用した。