Angularの公式チュートリアルをやるついでに、ホットリロードやMVVMなども学ぶ。



# はじめに

[前回の記事](https://qiita.com/tanakanata7190/items/308ff023d335188bf956)では、Angular on Docker環境を構築した。
アプリケーション名は `sample-angular-on-docker` という事にしている。

今回は、Angular公式チュートリアルのうち、[『チュートリアル: Tour of Heroes』](https://angular.jp/tutorial)を実施する。
本チュートリアルは、以下の章立てで進められる。

- イントロダクション
- プロジェクトの作成
- １．ヒーローエディター
- ２．リストの表示
- ３．フィーチャーコンポーネントの作成
- ４．サービスの追加
- ５．ナビゲーションの追加
- ６．サーバーからデータの取得

基本的にチュートリアルに沿っていくので、写経的な記事を書かず、
各章で詰まった点や、押さえておきたいポイントについて記事として残していく。



# 各種ポイント

本記事のタイトルの通り、チュートリアルをやるついでに色々と学んでいく。
ポイントとしては以下の通り。

- [POINT] ホットリロード
- [POINT] ディレクトリ構成
- [POINT] 「spec」って何？
- [POINT] `component` と `service` の違い
- [POINT] その他


## [POINT] ホットリロード

ファイル更新時にホットリロードしてくれると嬉しいなと思って調べてみると、起動オプションに `--poll` を設定してあげればよいとの事。
なので、起動時に以下を使うようにする。

```sh:sh
# こっちじゃなくて
root@60ff460a2f75:/projects/app# ng serve --host 0.0.0.0

# こっちを使う事で、ホットリロードができる
root@60ff460a2f75:/projects/app# ng serve --host 0.0.0.0 --poll 1  # 起動オプション --poll を指定
```


## [POINT] ディレクトリ構成

### ディレクトリ構成（プロジェクト作成直後）

プロジェクト作成直後のディレクトリ構成は以下の通り。

<details><summary>プロジェクト作成直後のディレクトリ構成</summary><div>

```sh:sh
.
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── .vscode
｜   └── （割愛）
├── node_modules
｜   └── （割愛）
└── src
     ├── app
     │   ├── app-routing.module.ts
     │   ├── app.component.css
     │   ├── app.component.html
     │   ├── app.component.spec.ts
     │   ├── app.component.ts
     │   └── app.module.ts
     ├── assets
     ├── environments
     │   ├── environment.prod.ts
     │   └── environment.ts
     ├── favicon.ico
     ├── index.html
     ├── main.ts
     ├── polyfills.ts
     ├── styles.css
     └── test.ts
```

</div></details>

### ディレクトリ構成（チュートリアル完了直後）

チュートリアルを進めていくと分かるが、 `component` や `service` などをAngular CLIで実行するたびに、 `app/` 配下に乱雑にファイルが生成される。


<details><summary>チュートリアル完了直後のディレクトリ構成</summary><div>

```sh:sh
.
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── .vscode
｜   └── （割愛）
├── node_modules
｜   └── （割愛）
└── src
     ├── app                <---------------------------- app配下が凄い事に！これじゃ管理できないよ X-(
     │   ├── dashboard
     │   │   ├── dashboard.component.css
     │   │   ├── dashboard.component.html
     │   │   ├── dashboard.component.spec.ts
     │   │   └── dashboard.component.ts
     │   ├── hero-detail
     │   │   ├── hero-detail.component.css
     │   │   ├── hero-detail.component.html
     │   │   ├── hero-detail.component.spec.ts
     │   │   └── hero-detail.component.ts
     │   ├── hero-search
     │   │   ├── hero-search.component.css
     │   │   ├── hero-search.component.html
     │   │   ├── hero-search.component.spec.ts
     │   │   └── hero-search.component.ts
     │   ├── heroes
     │   │   ├── heroes.component.css
     │   │   ├── heroes.component.html
     │   │   ├── heroes.component.spec.ts
     │   │   └── heroes.component.ts
     │   ├── messages
     │   │   ├── messages.component.css
     │   │   ├── messages.component.html
     │   │   ├── messages.component.spec.ts
     │   │   └── messages.component.ts
     │   ├── app-routing.module.ts
     │   ├── app.component.css
     │   ├── app.component.html
     │   ├── app.component.spec.ts
     │   ├── app.component.ts
     │   ├── app.module.ts
     │   ├── hero.service.spec.ts
     │   ├── hero.service.ts
     │   ├── hero.ts
     │   ├── in-memory-data.service.spec.ts
     │   ├── in-memory-data.service.ts
     │   ├── message.service.spec.ts
     │   ├── message.service.ts
     │   └── mock-heroes.ts
     ├── assets
     ├── environments
     │   ├── environment.prod.ts
     │   └── environment.ts
     ├── favicon.ico
     ├── index.html
     ├── main.ts
     ├── polyfills.ts
     ├── styles.css
     └── test.ts
```

</div></details>


### ディレクトリ構成（MVVM／最終形）

ソース管理していく上でディレクトリ構成は重要だが、ベストプラクティスが存在しない。
ググってみると、以下のような記事が見つかる。

- https://blog.lacolaco.net/2019/07/angular-ngmodule-and-directories/
- https://mya-ake.com/slides/regret-angular-web-app
- https://mattarishitemota.com/?p=510

「ドメイン単位」や「タイプ単位」で分けるように書かれているが、チュートリアルの時点だとそんなものは存在しない。
（いや、存在はしているんだけども、考えたくない）

AngularはMVVMアーキテクチャで構成されるので、そのままディレクトリにしてしまえばよいのでは？
という事で、MVVMに基づいてディレクトリを構成した。

<details><summary>MVVMで再構成したディレクトリ構成</summary><div>

```sh:sh
.
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── .vscode
｜   └── （割愛）
├── node_modules
｜   └── （割愛）
└── src
     ├── app                <---------------------------- だいぶ役割が分かりやすくなった :-)
     │   ├── app-routing.module.ts
     │   ├── app.module.ts
     │   ├── view
     │   │   ├── app.component.css
     │   │   ├── app.component.html
     │   │   ├── app.component.spec.ts
     │   │   ├── app.component.ts
     │   │   ├── dashboard
     │   │   │   ├── dashboard.component.css
     │   │   │   ├── dashboard.component.html
     │   │   │   ├── dashboard.component.spec.ts
     │   │   │   └── dashboard.component.ts
     │   │   ├── hero-detail
     │   │   │   ├── hero-detail.component.css
     │   │   │   ├── hero-detail.component.html
     │   │   │   ├── hero-detail.component.spec.ts
     │   │   │   └── hero-detail.component.ts
     │   │   ├── hero-search
     │   │   │   ├── hero-search.component.css
     │   │   │   ├── hero-search.component.html
     │   │   │   ├── hero-search.component.spec.ts
     │   │   │   └── hero-search.component.ts
     │   │   ├── heroes
     │   │   │   ├── heroes.component.css
     │   │   │   ├── heroes.component.html
     │   │   │   ├── heroes.component.spec.ts
     │   │   │   └── heroes.component.ts
     │   │   └── messages
     │   │       ├── messages.component.css
     │   │       ├── messages.component.html
     │   │       ├── messages.component.spec.ts
     │   │       └── messages.component.ts
     │   ├── viewmodel
     │   │  ├── hero.service.spec.ts
     │   │  ├── hero.service.ts
     │   │  ├── in-memory-data.service.spec.ts
     │   │  ├── in-memory-data.service.ts
     │   │  ├── message.service.spec.ts
     │   │  └── message.service.ts
     │   └── model
     │        ├── hero.ts
     │        └── mock-heroes.ts
     ├── assets
     ├── environments
     │   ├── environment.prod.ts
     │   └── environment.ts
     ├── favicon.ico
     ├── index.html
     ├── main.ts
     ├── polyfills.ts
     ├── styles.css
     └── test.ts
```

</div></details>


ちなみに、Angular CLIで `component` や `service` を作ると `app/` 配下に作られてしまうが、コマンド実行時にフォルダを作る事も可能。
なので、Angular CLIベースでコードをメンテナンスしていく際には、ディレクトリ構成を決めた上で、実行コマンドに制約をつけて運用するとよいと思う。

```sh:sh
# これだと app/ 配下に作られるので、管理しづらい
root@c773d67c1823:/projects/app# ng generate component heroes

# これだと app/view/ 配下に作られるので、管理しやすくなる
root@c773d67c1823:/projects/app# ng generate component view/heroes
```


## [POINT] 「spec」って何？

`component` や `service` をAngular CLIから生成すると、 `xxxx.component.spec.ts` なるファイルが生成される。

```sh:sh
# component の生成
root@c773d67c1823:/projects/app# ng generate component heroes
Node.js version v19.0.0 detected.
Odd numbered Node.js versions will not enter LTS status and should not be used for production. For more information, please see https://nodejs.org/en/about/releases/.

CREATE src/app/heroes/heroes.component.css (0 bytes)
CREATE src/app/heroes/heroes.component.html (21 bytes)
CREATE src/app/heroes/heroes.component.spec.ts (599 bytes)    <---------- なにこれ！！！！！！！！！！！！！！
CREATE src/app/heroes/heroes.component.ts (275 bytes)
UPDATE src/app/app.module.ts (475 bytes)

# service の生成
root@c773d67c1823:/projects/app# ng generate service hero
Node.js version v19.0.0 detected.
Odd numbered Node.js versions will not enter LTS status and should not be used for production. For more information, please see https://nodejs.org/en/about/releases/.

CREATE src/app/hero.service.spec.ts (347 bytes)    <---------- なにこれ！！！！！！！！！！！！！！
CREATE src/app/hero.service.ts (133 bytes)
```

この `xxxx.component.spec.ts` はチュートリアル内では使用しない。
使用しないからこそ、何のために生成されているのか意味が分からない。

調べてみると、単体試験実行用のファイルらしい。
以下、公式サイト。

- https://angular.jp/guide/testing-components-basics



## [POINT] `component` と `service` の違い

先ほどから `component` と `service` という単語を乱用していたが、そもそもこれらがよく分からない。

調べてみると、公式サイトに以下の記載があった。
以下、公式サイト。

- https://angular.jp/guide/architecture


> コンポーネントは ビュー を定義します。
> ビューは、プログラムのロジックとデータの中からAngularが選択し、変更できる画面要素のセットです。すべてのアプリケーションには、少なくともルートコンポーネントがあります。
>
> コンポーネントは、ビューに直接関係しない特定の機能を提供する サービス を使用します。
> サービスプロバイダーは、 依存性 としてコンポーネントに 注入 することができ、コードをモジュール化し、再利用可能で効率的にします。


なるほど。ここでMVVMの観点が生きてくる。
サービスがコンポーネントに注入するという事は、これがデータバインディングの仕組みなのだろうと思う。

なので、コンポーネントは `View` であり、サービスは `ViewModel` に相当する事が分かる。


### `View` と `ViewModel` は分かったけど、 `Model` は？

`Model` に相当するものは、おそらく `ng generate interface {name}` で生成されるものと思われる。
チュートリアル内だと `hero.ts` がそれに相当するが、コマンドではなく直接ファイルを作成するため、MVVM的観点だと分かりづらい。

試しに上記のコマンドを実行すると、以下のようにファイルが生成される。
（ `hero.ts` と同じ構成なので、これが `Model` なんだと思う。）

```sh:sh
# interface の生成
root@c773d67c1823:/projects/app# ng generate interface model/dummy
Node.js version v19.0.0 detected.
Odd numbered Node.js versions will not enter LTS status and should not be used for production. For more information, please see https://nodejs.org/en/about/releases/.
CREATE src/app/model/dummy.ts (27 bytes)

# ファイルの確認
root@c773d67c1823:/projects/app# cat src/app/model/dummy.ts
export interface Dummy {
}
```



## [POINT] その他

他にも、チュートリアルを進めていく上で、表面上の理解しかできず、使い方がいまいちピンと来ないものがあった。
まあ開発を進めていくうちに慣れていくだろうから、今は「こういうのがあるんだな」程度に留めておく。

- データバインディングには `ngModel` を用いる
    - `ngModel` を使用するには、 `FormsModule` のimportが必要
    - https://angular.jp/api/forms/NgModel
- classバインディングは、以下を参照
    - これは特に import が必要などは書かれていない
    - https://angular.jp/guide/class-binding
- `*ngFor` と `*ngIf` で html を操作
- ルーティングは `./app/app-routing.module.ts` で行われて、 `<router-outlet>` を使う



# チュートリアルを終えて

どうやって構築していくのかは分かったけど、肝心の仕組みだとが概念だとか規約だとかはチュートリアルでは身につかない。
ので、公式サイトの以下を参考に、勉強を進めていく。

- Angularの概念の紹介
    - https://angular.jp/guide/architecture
- Angularコーディングスタイルガイド
    - https://angular.jp/guide/styleguide
- 用語集
    - https://angular.jp/guide/glossary