1. ヒーローエディター　Angular チュートリアル

# 参考

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

# 前段

アプリケーションに基本的なタイトルが追加された。
次に、ヒーロー情報を表示するための新しいコンポーネントを作成し、そのコンポーネントをアプリケーションシェルに配置する。

# heroes コンポーネントを作成する 

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

```:Terminal
$ ng generate component heroes
```

CLI は　`src/app/heroes/` という新しいフォルダを作成し、 `HeroesComponent` に関する3つのファイルをテストファイルと一緒に生成する。

`HeroesComponent` のクラスファイルは次の通り。

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

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}
```

常に Angular コアライブラリから `Component` シンボルをインポートし、コンポーネントクラスに `@Component` で注釈をつける。

`@Component` は、コンポーネントの Angular メタデータを指定するデコレーター関数。

CLI は3つのメタデータプロパティを生成した。

1. `selector` - コンポーネントの CSS 要素セレクター
1. `templateUrl` - コンポーネントのテンプレートファイルの場所
1. `styleUrls` - コンポーネントのプライベート CSS スタイルの場所

[CSS 要素セレクタ](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors) である `'app-heroes'` は、親コンポーネントのテンプレート内でこのコンポーネントを識別する HTML 要素の名前と一致する。

`ngOnInit()` は [ライフサイクルフック](https://angular.jp/guide/lifecycle-hooks#oninit) 。
Angular は、コンポーネントの作成直後に `ngOnInit()` を呼び出す。
初期化ロジックを置くのに適している。

常にコンポーネントクラスを `export` するので、 `AppModule` のようにいつでも他の場所に `import` できる。

## hero プロパティを追加する

"Windstorm" という名前のヒーローのために、 `HeroesComponent` に `hero` プロパティを追加する。

```typescript:heroes.components.ts
hero = 'Windstorm';
```

## ヒーローを表示する

`heroes.component.html` テンプレートファイルを開く。
Angular CLI で生成されたデフォルトのテキストを削除し、それを新しい `hero` プロパティへのデータバインディングに置き換えよう。

```html:heroes.component.html
<h2>{{hero}}</h2>
```

# HeroesComponent ビューを表示する

`HeroesComponent` を表示するには、それをアプリケーションシェルの `AppComponent` のテンプレートに追加する必要がある。

`app-heroes` は `HeroesComponent` の [要素セレクタ](https://angular.jp/tutorial/toh-pt1#selector) であることを思い出そう。
なので、 `AppComponent` のテンプレートファイルで、タイトルの直下に `<app-heroes>` 要素を追加しよう。

```html:src/app/app.component.html
<h1>{{title}}</h1>
<app-heroes></app-heroes>
```

CLI の `ng serve` コマンドがまだ実行中であれば、ブラウザが更新され、アプリケーションのタイトルとヒーローの名前が表示される。

# Hero インターフェースを作成する

本当のヒーローは名前だけではない。

`src/app` フォルダ内の独自のファイルに、 `Hero` インターフェースを作成する。
それに `id` プロパティ と `name` プロパティを与える。

```typescript:src/app/hero.ts
export interface Hero {
  id: number;
  name: string;
}
```

`HeroesComponent` クラスに戻り、 `Hero` インターフェースをインポートする。
コンポーネントの `hero` プロパティを `Hero` 型にリファクタリングする。
それを、 `1` という `id` と `Windstorm` という名前で初期化する。

改定された `HeroesComponent` のクラスファイルは、次のようになる。

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

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };

  constructor() { }

  ngOnInit() {
  }

}
```

ヒーローを文字列からオブジェクトに変更したため、ページが正しく表示されなくなった。

# ヒーローオブジェクトを表示する

ヒーローの名前を知らせるためにテンプレートのバインディングを更新し、次のような詳細レイアウトで `id` と `name` の両方を表示する。

```html:heroes.component.html
<h2>{{hero.name}} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>
```

ブラウザが更新され、ヒーローの情報が表示される。

# *UppercasePipe* で書式設定する

`hero.name` のバインディングをこのように修正しよう。

```html:src/app/heroes/heroes.component.html
<h2>{{hero.name | uppercase}} Details</h2>
```
ブラウザが更新され、ヒーローの名前が大文字で表示されるようになる。

補間バインディングの中、パイプ演算子 (|) の直後にある単語 `[uppercase](https://angular.jp/api/common/UpperCasePipe)` は、組み込みの `UppercasePipe` を起動する。

[パイプ](https://angular.jp/guide/pipes) は、文字列、通貨金額、日付や、その他の表示データを書式設定するのに適している。
Angular は複数のビルトインパイプを備えており、自分で独自のパイプを作ることもできる。

# ヒーローを編集する

ユーザーは `<input>` テキストボックスでヒーローの名前を編集できるべき。

テキストボックスにはヒーローの `name` プロパティが *表示* され、ユーザーの入力時にそのプロパティが *更新* される。
これは、コンポーネントクラスから *画面* へ、そして画面から *コンポーネントクラス* へのデータフローを意味する。

そのデータフローを自動化するには、 `<input>` フォーム要素と `hero.name` プロパティとの間に双方向データバインディングを設定する。

## 双方向データバインディング

`HeroCOmponent` テンプレートの詳細エリアをリファクタリングすると、次のようになる。

```html:src/app/heroes/heroes.component.html
<div>
  <label for="name">Hero name: </label>
  <input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
```

[(ngModel)] は、 Angular の双方向データバインディング構文。

これで `hero.name` プロパティを HTML のテキストボックスにバインドするので、 `hero.name` プロパティからテキストボックスへ、テキストボックスから `hero.name` プロパティへ、 *双方向* にデータを流すことができる。

## 見つからない *FormsModule*

`[(ngModel)]` を追加したときにアプリケーションが動かなくなったことに注目しよう。

エラーを表示するには、ブラウザの開発ツールを開き、コンソールで次のようなメッセージを探す。

```:Terminal
Error: src/app/heroes/heroes.component.html:5:20 - error NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'.
```

`[ngModel](https://angular.jp/api/forms/NgModel)` は有効な Angular ディレクティブだが、デフォルトでは使用できない。

これはオプションの `[FormsModule](https://angular.jp/api/forms/FormsModule)` に属しており、使用するにはそのモジュールをオプトインする必要がある。

# *AppModule*

Angular では、アプリケーションの部品がどのように合わさるかや、アプリケーションの部品がどのように合わさるかや、アプリケーションが必要としている他のファイルやライブラリを知る必要がある。
この情報を *メタデータ* という。

一部のメタデータは、コンポーネントクラスに追加した `@Component` デコレーター内にある。
その他の重要なメタデータは `@NgModule` デコレーター内にある。

最も重要な `@NgModule` デコレーターは、トップレベルの AppModule クラスに注釈をつける。

Angular CLI は、プロジェクトを作成するときに `src/app/app.module.ts` に `AppModule` クラスを作成した。
ここで `[FormsModule](https://angular.jp/api/forms/FormsModule)` をオプトインする。

## *FormsModule* をインポートする

`AppModule` (`app.module.ts`) を開き、 `@angular/forms` ライブラリから `FormsModule` シンボルをインポートする。

```typescript:app.module.ts
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
```

それから、 `FormsModule` を `@NgModule` メタデータの `imports` 配列に追加する。
この配列には、アプリケーションに必要な外部モジュールのリストが含まれている。

```typescript:app.module.ts
imports: [
  BrowserModule,
  FormsModule
],
```

ブラウザが更新されると、アプリケーションは再び動作するはず。
ヒーローの名前を編集し、テキストボックスの上にある `<h2>` に即座に変更が反映されていることを確認できる。

(ほんとだ・・・すごい・・・)

## HeroesComponent を宣言する

すべてのコンポーネントは、 *ただ1つの* [NgModule](https://angular.jp/guide/ngmodules) で宣言されなければならない。

*自分では* `HeroesComponent` を宣言していない。
では、なぜアプリケーションは動作したのだろうか？

それは、アプリケーションが動作したのは、 Angular CLI が `HeroesComponent` を生成した時に、 `AppModule` でそのコンポーネントの宣言を行っていたからだ。

`src/app/app.module.ts` を開き、先頭付近で `HeroesComponent` がインポートされているのを見つけよう。

```typescript:src/app/app.modules.ts
import { HeroesComponent } from './heroes/heroes.component';
```

`HeroesComponent` は、 `@NgModule.declarations` 配列で宣言されている。

```typescript:src/app/app.module.ts
  declarations: [
    AppComponent,
    HeroesComponent
  ],
```

`AppModule` は `AppComponent` と `HeroesComponent` の両方のアプリケーションコンポーネントを宣言している。

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

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

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

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };

  constructor() { }

  ngOnInit() {
  }

}
```

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

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

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

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

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Tour of Heroes';
}
```

```html:src/app/app.component.html
<h1>{{title}}</h1>
<app-heroes></app-heroes>
```

```typescript:src/app/hero.ts
export interface Hero {
  id: number;
  name: string;
}
```

# まとめ

* CLI を使用して、2番目の `HeroesComponent` を作成した。
* `HeroesComponent` を `AppComponent` シェルに追加して表示した。
* 名前をフォーマットするために、 `UppercasePipe` を適用した。
* `ngModel` ディレクティブで双方向データバインディングを使用した。
* `AppModule` について学んだ。
* `AppModule` に `FormsModule` をインポートして、 Anguler `ngModule` ディレクティブを認識して適用するようにした。
* `AppModule` でコンポーネントを宣言することの重要性を学び、 CLI が自分のためにその宣言を行っていることを認識した。