Angularの学習環境を構築する

# **はじめに**
今日はAngularの学習環境を構築したいと思います。先日、下記投稿でLaravelの学習環境を構築したばかりですが、Angularの学習も必要になったので、いつものDocker環境上で作りつつ、覚書しておきたいと思います。Angularを学ぶという事は同時にTypeScriptの知識も必要になりますので、一気に頭の中で情報が溢れてきておりますが、何とか食らいついていきたいと思います（JavaScriptの経験はそこそこあるのでそこは救いです）。また、本来はNode.jsもしっかり理解しないといけないと思うのですが、今回はAngularの学習に重きを置きたいので、Node.jsは環境構築に必要な知識に留め、深追いはしないようにします。

https://qiita.com/829yasubee/items/e6d9694d1c507252d844

# **環境周りの情報**
いつも使用している図もアップデートして載せておきます。今回はコンテナ１つ構成（WEBサーバー）のみとなる予定です。
しかし、これだけ色々な環境を１つのマシン内で構築できて、なおかつスピーディーに作り上げる事ができるのは、DockerやVSCodeのおかげです。ほんと助かっております（しみじみ・・・）。
![スクリーンショット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/28be9afe-528c-4377-8ee9-c7134b8992d7.png)

# **環境構築手順**
今回も学習環境を作りたいだけなので、あまり複雑な構成にはせず、なるべく簡単に済ませたいと思います。
今回のゴールは、Angularのトップページが表示されるところまでとします。
事前に色々と調査・検討した結果、次のような構成で環境構築したいと思います。

- WEBサーバーは、UbuntuのDockerイメージを基に、Node.jsとAngular CLIを追加インストールしてコンテナ作成
- WEBサーバーのコンテナにアクセスし、Angular CLIコマンドを使用してAngularプロジェクトを作成

WEBサーバーとは言いつつも、Apache等のちゃんとしたWEBサーバーを入れるわけではなく、Angular内部に用意されている開発・テスト用の簡易WEBサーバーを使いたいと思います。

### **各種定義ファイルの作成**
今回作成する各種定義ファイルに関しても、過去投稿で既出の部分は特に記載せず、新しい部分だけに記載を絞りたいと思います。端折ってしまう部分に関しては、過去投稿をご参照下さい。
それでは、いつも通り、定義ファイルの配置ツリー構造から紹介します。こんな感じです。
![スクリーンショッ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/3d9b962d-c39d-ed25-67ed-0fd2ed19712a.png)

次に、各種定義ファイルの中身です。今回はいつもよりシンプルです。

**devcontainer.json**
コンテナ名等の名称違いを除き、ほぼいつもと同じです。インストールする拡張機能は異なっており、今回は"Angular Language Service"拡張機能を入れます。これはAngularでプログラミングする際、コード補完等を行ってくれるものです。こういうがあるのも非常にありがたいです。

```json:.devcontainer/devcontainer.json
{
    "name": "angular",
    "dockerComposeFile": "../docker-compose.yml",
    "service": "web",
    "workspaceFolder": "/var/www/html",
    "extensions": [
		"Angular.ng-template"
	],
    "remoteUser": "${localEnv:USER}"
}
```

**Dockerfile**
今回使用するDockerイメージはubuntuにしました。本当はNode.jsの公式イメージを使おうと思ったのですが、このイメージはnodeユーザー(1000)とnodeグループ(1000)を作ってしまっている為、私の環境の場合、Dockerホストの作業ユーザーと被ってしまう問題が発生する為、自分でNode.jsをインストールするところからやります。ちなみに、現時点(2022/4/24)でのNode.jsの最新バージョンは18ですが、Angular CLI側がまだそのバージョンには未対応でしたので、直近のLTSであるバージョン16を使用する事にします。
Node.jsとnpmをインストール後は、RUNコマンドでAngular CLIをgオプションでインストールし、/varディレクトリ配下にwwwディレクトリを作成する流れとなっています(手順後半でAngularプロジェクトを作成する予定のディレクトリ)。

```Dockerfile:build/web/Dockerfile
FROM ubuntu:latest

ARG USER_UID
ARG USER_NAME
ARG GROUP_GID
ARG GROUP_NAME

RUN groupadd -g ${GROUP_GID} ${GROUP_NAME} \
  && useradd -m -s /bin/bash -u ${USER_UID} -g ${GROUP_GID} ${USER_NAME} \
  && apt-get update \
  && apt-get install -y sudo \
  && echo "${USER_NAME} ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/${USER_NAME} \
  && chmod 0440 /etc/sudoers.d/${USER_NAME}

RUN apt-get update \
  && apt-get install -y curl git

RUN curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - \
  && apt-get install -y nodejs \
  && npm install -g npm

RUN npm install -g @angular/cli

RUN cd /var \
  && mkdir www

USER ${USER_NAME}
```

:::note info
gオプションについて補足。
gオプションを付けるとグローバルインストールになり、gオプションを付けないとローカルインストールになります。
私の理解をWindowsでアプリケーションをインストールする場合で例えると、グローバルインストールはインストーラーで全ユーザー使用可能な状態でインストールするパターンで、ローカルインストールはZipファイルとかでダウンロードしてきたアプリケーションを自分の好きなフォルダに展開して絞られたスコープで利用するパターン、だと理解しました(違っていたらすみません)。
だから、同一マシン内で複数Angular環境をバージョン違いで構築する場合はローカルインストールしないとダメといった感じです。今回、私は独立したコンテナ内に環境構築しているのでグローバルインストールにしました。
:::

**.env**
いつもよりさらにシンプルになっています。特筆すべき箇所はありません。

```text:.env
WEB_FORWARD_PORT={Webブラウザからのアクセス用フォワードポート}

USER_UID={DockerホストOSの一般ユーザーのUID}
USER_NAME={DockerホストOSの一般ユーザーのユーザー名}
GROUP_GID={DockerホストOSの一般ユーザーのGID}
GROUP_NAME={DockerホストOSの一般ユーザーのグループ名}
```

**docker-compose.yml**
今回は、WEBサーバーコンテナ１つ構成です。手順後半で、バインドマウントしているディレクトリにAngularプロジェクトを作成します。
それ以外はいつもと同じ感じの内容ですので割愛します。

```yml:docker-compose.yml
version: '3'
services:
  web:
    build:
      context: ./build/web/
      dockerfile: Dockerfile
      args:
        USER_UID: ${USER_UID}
        USER_NAME: ${USER_NAME}
        GROUP_GID: ${GROUP_GID}
        GROUP_NAME: ${GROUP_NAME}
    container_name: angular_web
    hostname: angular_web
    networks:
      - net
    volumes:
      - ./data/web:/var/www/html
    ports:
      - ${WEB_FORWARD_PORT}:4200
    environment:
      TZ: Asia/Tokyo
    tty: true
networks:
  net:
    name: angular_net
```

### **コンテナの作成**
今回も前回の投稿と同様に、コマンドベースではなく、VSCodeの[Compose Up]メニューを使用します。
VSCodeの左側タブで"エクスプローラー"タブに切り替え、docker-compose.ymlファイルを右クリックし、[Compose Up]を選択。すると、コンテナの生成が始まり、進捗状況がターミナルウィンドウに表示されます。終わるまで待ちます。
![スクリーンショット3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/d410f83a-9a9e-869a-9729-27f23ef273fa.png)
ターミナルウィンドウにエラー表示なくコンテナの生成が終わったら、無事コンテナが生成されているか確認してみましょう。
VSCodeの左側タブで"Docker"タブに切り替えます。カスタムイメージとコンテナとネットワークが生成され、コンテナが起動しています。
![スクリーンショット4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/12b40ce1-268b-d9a0-b746-7759f3e77677.png)

:::note info
今回生成したコンテナはWEBサーバーをサービスとして常時起動する作りにはなっていません（Angularの簡易WEBサーバーを使う為）。その為、VSCodeを起動する度にコンテナをStartさせてからコンテナ内部にアクセスするようにして下さい。やり方は、VSCodeの左側タブで"Docker"タブに切り替え、該当コンテナを右クリックし、[Start]を選択するだけです。
:::

### **Angularプロジェクトの生成**
それでは、Angularプロジェクトの生成をしたいと思います。
まず、VSCodeでリモートコンテナにアクセスします。
VSCodeの画面左下[><]→[Reopen in Container]を選択。コンテナ内に入れます。
![スクリーンショット5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/d92d4476-8c86-1868-0e7a-8e6502b6ed88.png)
VSCodeの画面左下[><]より、Angularコンテナに入れた事が確認できます。ただし、まだAngularプロジェクトは生成していませんので、エクスプローラーは空の状態です。
![スクリーンショット6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/5d0549ea-d252-3da3-ef17-3f067e349ff1.png)
VSCodeでターミナルウィンドウを開きます。カレントディレクトリが"/var/www/html"になっている事を確認して下さい。

:::note info
リモートコンテナへアクセス時は、カレントディレクトリが"/var/www/html"となるようにdevcontainer.jsonで定義しています。もし、カレントディレクトリが異なる場合には、"/var/www/html"ディレクトリに移動してから次の手順を実行して下さい。
:::

VSCodeのターミナルウィンドウで、次のコマンドを実行します。

```bash:実行コマンド
$ ng new html --directory=./
```

:::note info
directoryオプションについて補足。
今回構築している私の環境の場合、/var/wwwディレクトリまではrootユーザーが所有権のディレクトリとし、htmlディレクトリから普段使う作業ユーザーが所有権のディレクトリにして、普段コンテナアクセスする際の初期ディクレクトリを/var/www/htmlにしたかった為、Angularプロジェクト生成前にhtmlディレクトリを作成しています。しかし、Angularプロジェクト生成コマンドにdirectoryオプションを付与しないと、コマンド引数で指定したディレクトリをカレントディレクトリに作成して、その中にAngularプロジェクトが生成されてしまい、自分の意図しないディレクトリが１階層余計に作られてしまいます。これを防止する為、directoryオプションで現在のカレントディレクトリに直接Angularプロジェクトを生成して下さいと指定しています。
:::

コマンドを実行すると、Angularプロジェクト生成に関して、下記のような質問をされます。
ルーティング機能は必要だと思いますので"Yes"、使用するスタイルシートはデフォルトの"CSS"にしました。

```text:Angularプロジェクト生成に関する質問
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? (Use arrow keys)
❯ CSS
  SCSS   [ https://sass-lang.com/documentation/syntax#scss                ]
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
  Less   [ http://lesscss.org                                             ]
```

### **Angularプロジェクトの実行**
ターミナルウィンドウにエラー表示なくAngularプロジェクトの生成が終わったら、Angularがちゃんと動作しているか確認してみましょう。
まず、次のコマンドを実行します。

```bash:実行コマンド
$ ng serve --host 0.0.0.0
```

:::note info
私の環境の場合、Dockerをホストしているマシン外よりHTTPアクセスする必要がある為、ng serveコマンドに--host 0.0.0.0を付けています。スタンドアロン環境でDocker環境を構築している方は、このオプションは不要です。
:::

:::note warn
--host 0.0.0.0を付けてWEBサーバーを起動すると警告メッセージ（このWEBサーバーはあくまで簡易的な開発・テスト用途だから、全てのホストからの接続を許可するのは如何なものか的なメッセージ）が表示されますが、外部から接続できない環境で実行しているので、私の場合には特に気にせず続行しました。皆様はそれぞれの環境構成に応じてご判断下さい。
:::

ブラウザを起動し、下記形式のURLへアクセスして下さい。

```text:アクセスURL
http://{WEBサーバーのIPアドレスまたはホスト名}:{.envファイルのWEB_FORWARD_PORTの設定値}/
```

下記画像のようなAngularのデフォルト画面が表示されればOKです。
![スクリーンショット7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206277/49529051-2b74-fb64-0d79-791faccb2d1a.png)

:::note info
サーバーを止めたい時は、[Ctrl + C]キーで止まります。
尚、今回使用しているAngularの簡易WEBサーバーは、毎回自分で起動と停止を行う必要があります。Apache等のように自動起動するようにはしていませんので。
:::

# **最後に**
Node.jsの公式イメージファイルが使えなかったので意外に苦戦しましたが、何とか学習環境を構築する事が出来ました。本来はちゃんとApache等のWebサーバーをインストールし、環境構築するものだと思いますが、今回の目的はAngularの学習が目的なので、これでよしにしたいと思います。
お疲れ様でした！