趣旨
もともと、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
が設定されています。
"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
要素を読み込んでメンバ変数にセットしています。
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へのリンクをリストで出力します。
<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で作成されているので、ファイルを編集します。
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は読み込みません。
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
要素の配列を与えます。
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`
# 実行結果
サンプルコードを実行します。
## 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)