AngularでEnvironmentを使って複数環境のアプリをデプロイする


# 趣旨

もともと、Angular（1.x）でアプリを開発していて、似たような機能をもつ2つのアプリをリリースしていました。
それぞれいくつかのControllerを使っていたのですが、下記のように共通している機能（ページ）が多いアプリでした。

| | アプリA | アプリB |
| :--- | :--: | :--: |
| Controller A | ○ | ○ |
| Controller B |  | ○ |
| Controller C |  | ○ |
| Controller D |  | ○ |
| Controller E | ○ |  |

リポジトリも複数に分けて管理していて、とても管理がかっこ悪い状態でした。
そこで、このアプリをAngular（2.x）に移行し、それぞれをComponent化することでリポジトリを一つで管理できるようにすることを考えました。

# 方針

Angular（2.x）のビルドにAngular CLIを使っている場合、ビルド時に`environment`を使って環境を切り替えることができます。
Angular CLIで作成したコードは、初期状態で以下の2つの`environment`が設定されています。

```angular-cli.json
"environments": {
  "source": "environments/environment.ts",
  "dev": "environments/environment.ts",
  "prod": "environments/environment.prod.ts"
}
```

この状態では、開発環境と本番環境の2つしか存在していません。
この本番環境を複数用意することで、複数のアプリに対応させます。

# サンプル

## 構成

今回は4つのComponentをもつアプリを作ります。
Prodという環境とProd-limitedという環境のアプリを作成します。

| | Prod | Prod-limited |
| :--- | :--: | :--: |
| Menu | ○ | ○ |
| Component1 | ○ | ○ |
| Component2 | ○ |  |
| Component3 | ○ | ○ |

## Componentの作成

Angular CLIをインストールしていない場合は、下記のコマンドであらかじめインストールしておいてください。

```
npm install -g angular-cli
```

まず、ベースとなるコードを作成します。

```
ng new angular-multi-build-sample
```

Angular CLIを使って作成します。

```
ng g component menu
ng g component component1
ng g component component2
ng g component component3
```

### `menu.component.ts`

ここはあってもなくてもいいのですが、Componentが使えるのかどうかというのをわかりやすくするために、読み込んでいるComponentへのリンクをまとめたヘッダーメニューを作成します。

生成された`menu.component.ts`を編集します。
ここでは、`app.module.ts`と同様に`environment`の`routes`要素を読み込んでメンバ変数にセットしています。

```menu.component.ts
import { Component, OnInit } from '@angular/core';
import { Routes } from '@angular/router';
import { environment } from '../../environments/environment';

@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {
  private routes: Routes = [];

  constructor() {
    this.routes = environment.routes;
  }

  ngOnInit() { }
}
```

### `menu.component.html`

そして、`menu.component.html`に読み込まれているComponentへのリンクをリストで出力します。

```menu.component.html
<ul>
  <li *ngFor="let route of routes">
    <a routerLink="{{route.path}}">{{route.path}}</a>
  </li>
</ul>

```

## 各環境の設定

### `environment.prod.ts`

Prod環境用のEnvironmentを編集します。
`environment.prod.ts`はAngular CLIで作成されているので、ファイルを編集します。

```environment.prod.ts
import { Component1Component } from '../app/component1/component1.component';
import { Component2Component } from '../app/component2/component2.component';
import { Component3Component } from '../app/component3/component3.component';

export const environment = {
  production: true,
  routes: [
    {
      path: 'component1',
      component: Component1Component
    }, {
      path: 'component2',
      component: Component2Component
    }, {
      path: 'component3',
      component: Component3Component
    }
  ]
};

```

この環境で使用するComponentを読み込んで`Routes`として扱うオブジェクト配列を用意します。

### `environment.prod-limited.ts`

次に、Prod-limited環境用のEnvironmentを作成します。
`environment`配下に`environment.prod-limited.ts`を作成し、`environment.prod.ts`と同様にComponentの読み込みとRoutes用の配列を定義します。
ただし、Prod-limited環境ではComponent1とComponent3だけ使用できる環境としたいため、Component2は読み込みません。

```environment.prod-limited.ts
import { Component1Component } from '../app/component1/component1.component';
import { Component2Component } from '../app/component2/component2.component';
import { Component3Component } from '../app/component3/component3.component';

export const environment = {
  production: true,
  routes: [
    {
      path: 'component1',
      component: Component1Component
    }, {
      path: 'component3',
      component: Component3Component
    }
  ]
};

```

## ルーティングの設定

### `app.module.ts`

実際にルーティングを行う部分を実装します。
`RouterModule`に与える`Routes`配列に、先程定義した`environment`の`routes`要素の配列を与えます。

```app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule, Routes } from '@angular/router';
import { environment } from '../environments/environment';

import { AppComponent } from './app.component';
import { Component1Component } from './component1/component1.component';
import { Component2Component } from './component2/component2.component';
import { Component3Component } from './component3/component3.component';
import { MenuComponent } from './menu/menu.component';

const routes: Routes = environment.routes;

@NgModule({
  declarations: [
    AppComponent,
    Component1Component,
    Component2Component,
    Component3Component,
    MenuComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

```

また、`MenuComponent`を表示できるようにするために、`declarations`に追記しておきます。

### `app.component.html`

最後に、各Componentを表示する設定をします。
`app.component.html`に`app-menu`と`router-outlet`を記述します。
`app-menu`タグは先程作成した`MenuComponent`が挿入されます。
`router-outlet`タグはURLのパスに応じて対応したComponentを挿入します。

```app.component.html`
<app-menu></app-menu>
<router-outlet></router-outlet>
```

# 実行結果

サンプルコードを実行します。

## Prod


下記コマンドで開発用サーバーを起動します。

```
ng serve -e prod
```

すべてのComponentがメニューに表示されており、リンクをクリックして遷移することができます。

![スクリーンショット 2017-01-01 14.40.55.png](https://qiita-image-store.s3.amazonaws.com/0/68239/28c34796-6328-61f3-86b1-9f68ab851d5a.png "スクリーンショット 2017-01-01 14.40.55.png")

## Prod-limited

同様にオプションの引数を変えてサーバーを起動します。

```
ng serve -e prod-limited
```

Component2がメニューには表示されず、`http://localhost:4200/component2`にアクセスすることもできません。

![スクリーンショット 2017-01-01 14.41.32.png](https://qiita-image-store.s3.amazonaws.com/0/68239/4bbd7fec-bada-3eb7-3da1-5c5c8ef494a7.png "スクリーンショット 2017-01-01 14.41.32.png")

# まとめ

Angular CLIのEnvironmentを使うことで複数環境向けにアプリをビルドすることができました。
私がこの機能を実際に使ったのはあるアプリで管理者用とその他利用者用で利用できる機能を制限したものとそうでないものをデプロイする必要があったからです。（冒頭の表のようなイメージ）

きちんとアプリの機能制限を実装するのであれば、バックエンドとうまく連携させて権限によって制御をかけるのが望ましいとは思います。
今回私が対象としていたアプリはバックエンドに複数のAPIを組み合わせていたり、そこまで厳密な認証等は不要であったため今回の方法を採用しています。

今回のソースコードはここにおいています。

[mukopikmin/angular-multi-build-sample](https://github.com/mukopikmin/angular-multi-build-sample)

# 参考

 * [Quickstart - ts - QUICKSTART](https://angular.io/docs/ts/latest/quickstart.html)
 * [angular/angular-cli: CLI tool for Angular](https://github.com/angular/angular-cli)