Angular-CLI チートシート
@armorik83 です。今回はangular-cli
についてまとめます。
angular-cli
Prototype of a CLI for Angular 2 applications based on the ember-cli project.
angular-cli
は Angular の 2.x 以上(本稿では Angular と記述する)を用いたアプリケーションの開発を補助する CLI ツールである。
インストール
インストールは至って簡単だ。
npm i -g angular-cli
node_modules
のグローバル空間にインストールするため抵抗がある人もいるかもしれないが、その場合適宜工夫してもらいたい。基本的にコマンドラインツールとして使うので、筆者はあまり問題ないと考えているが、バージョン違いによる挙動の差異があり得るため、チームで運用する際はメンバー間でバージョンについて注意されたい。
angular-cli の必然性
拙記事に『npm i して Angualr 2 の Hello World!を書くところまで』というものがあるが、現時点ではこの記事の内容よりangular-cli
によって生成された基礎の方が使いやすい。利点を挙げる。
- Angular 公式が良と考えているディレクトリ構成に従える
-
webpackなどのバンドラーの環境構築を自動で行うため意識しなくてよい
-
ng serve
による開発中のブラウザ上でのプレビューが高速 - AoTを前提としているビルド
-
- Lint やテストの環境構築が不要ですぐに利用可能
一方で、これらはそのまま欠点にもなりうる。
- 独自のドメインレイヤーが多い場合どこに置けばいいかの指標を
angular-cli
は提示しない - Angular で完結しない場合、たとえば Babel のトランスパイルを別途挟む必要があるときなど、webpack の設定がブラックボックス化されているため拡張が面倒
- Lint はTSLint、テストフレームワークはJasmineに固定される
ただし、JavaScript や Angular の熟練者であれどもangular-cli
が提供するメリットがこれらの欠点を上回っている(と筆者は感じている)ため、多少の束縛を踏まえてangular-cli
を前提とした開発フローを検討するほうがよい。
新規作成
angular-cli
を用いたアプリケーションの新規作成はコマンドで行える。
ng new myapp
myapp
は任意のアプリケーション名だ。このコマンドを実行することで npm によるインストールと各種ファイルの自動生成を行う。では、このときの生成結果をみてみよう。なお、本稿でのangular-cli
のバージョンは1.0.0-beta.24
である。
.
├── README.md
├── angular-cli.json
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.json
├── karma.conf.js
├── node_modules
│ ├── @angular
│ │ ├── common
│ │ ├── compiler
│ │ ├── compiler-cli
│ │ ├── core
│ │ ├── forms
│ │ ├── http
│ │ ├── platform-browser
│ │ ├── platform-browser-dynamic
│ │ ├── router
│ │ └── tsc-wrapped
│ ├── @angular-cli
│ │ ├── ast-tools
│ │ └── base-href-webpack
│ ├── @ngtools
│ │ └── webpack
│ ├── @types
│ │ ├── jasmine
│ │ ├── node
│ │ ├── q
│ │ └── selenium-webdriver
│ ├── abbrev
省略
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── 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
│ └── tsconfig.json
└── tslint.json
angular-cli.json
e2e/
karma.conf.js
protractor.conf.js
tslint.json
これらの自動生成が特に嬉しい。e2e 周りは、環境構築を怠りそのまま実施しない例も多いと予想するので、最初から準備されているならば取り組みやすいだろう。.gitignore
も生成されているのは地味に嬉しい点だ。
次にsrc/
内をみていく。
.
├── app
│ ├── 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
└── tsconfig.json
基本的にはapp/
以下にアプリケーションのコードが格納されるという認識でよい。
-
assets/
- 画像や、どの Component にも紐つかない CSS、
i18n
言語ファイルなどを格納すればいいはず
- 画像や、どの Component にも紐つかない CSS、
-
index.html
,main.ts
,styles.css
,test.ts
- それぞれのエントリポイントとなるファイル
- 特に
test.ts
は自前で記述するとかなり面倒なので助かる
各種自動生成のチートシート
本稿の主題でもあるチートシートを記録する。angular-cli
ではng g component my-new-component
のようにコマンドを実行することで、その Angular アプリケーションに新しく Component や Service を追加できる。そのときの生成先や生成ファイル数などを思い出すためのものだ。
ng g component
ng g component [name]
は Component を新規に作成する。
ng g component foo-bar-baz
installing component
create src/app/foo-bar-baz/foo-bar-baz.component.css
create src/app/foo-bar-baz/foo-bar-baz.component.html
create src/app/foo-bar-baz/foo-bar-baz.component.spec.ts
create src/app/foo-bar-baz/foo-bar-baz.component.ts
update src/app/app.module.ts
標準では、常にcss
, html
, テスト用spec.ts
, ts
の 4 つをセットで生成し、それらを 1 ディレクトリに格納する。ng g component
似続けてキャメルケース(CamelCase)
を入力しても自動的にケバブケース(kebab-case)
に変換される。ここにmy-component
などと入力すればMyComponentComponent
という class が出力されるため、component
を名前に含む必要はない。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-foo-bar-baz',
templateUrl: './foo-bar-baz.component.html',
styleUrls: ['./foo-bar-baz.component.css']
})
export class FooBarBazComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
ts
ではselector
に常にapp-
接頭辞が付与される。templateUrl
とstyleUrls
は自動的に記述されている。そしてapp.module.ts
に自動的にこの Component が読み込まれるようになっている。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { FooBarBazComponent } from './foo-bar-baz/foo-bar-baz.component';
@NgModule({
declarations: [
AppComponent,
FooBarBazComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ng g directive
ng g directive [name]
は Directive を新規に作成する。
ng g directive foo
installing directive
create src/app/foo.directive.spec.ts
create src/app/foo.directive.ts
update src/app/app.module.ts
標準的にはapp/
直下に配置されることに注意されたい。これを回避したい場合、例えばapp/directives/
下に配置したければ次のようにする。
mkdir ./src/app/directives && cd ./src/app/directives
ng g directive foo
installing directive
create src/app/directives/foo.directive.spec.ts
create src/app/directives/foo.directive.ts
update src/app/app.module.ts
要するに Current Directory を考慮して生成する。
pwd
/Users/armorik83/Desktop/myapp
ng g directive foo --flat false
installing directive
create src/app/foo/foo.directive.spec.ts
create src/app/foo/foo.directive.ts
update src/app/app.module.ts
Root で--flat false
を付けることで[name]/[name].directive.ts
を生成する。このオプションも Current Directory を考慮する。オプションについては次節にてまとめる。
import { Directive } from '@angular/core';
@Directive({
selector: '[appFoo]'
})
export class FooDirective {
constructor() { }
}
ts
側はselector
が[appFoo]
とキャメルケース指定になっている点に注意だ。
ng g pipe
ng g pipe [name]
は Pipe を新規に作成する。Directive とほぼ同様である。
ng g pipe foo
installing pipe
create src/app/foo.pipe.spec.ts
create src/app/foo.pipe.ts
update src/app/app.module.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'foo'
})
export class FooPipe implements PipeTransform {
transform(value: any, args?: any): any {
return null;
}
}
ng g service
ng g service [name]
はInjectable
な class を新規に作成する。
ng g service foo
installing service
create src/app/foo.service.spec.ts
create src/app/foo.service.ts
WARNING Service is generated but not provided, it must be provided to be used
ディレクトリ構成だが、app/
以下に直接配置するのは規模拡大時に大量に並ぶことが想定されるため避けたい。筆者の経験則としてはapp/services/
とするかapp/foo/foo.component.ts
と同階層にapp/foo/foo.service.ts
としたい。この辺りは作成しようとする Service の性質に合わせて考えればよいだろう。
そしてWARNING Service is generated but not provided, it must be provided to be used
とあるように、app.module.ts
に自動で追記されない点には注意すべきである。これは Service を一律で provide するか Component 単位で provide するか開発者が判断せねばならないためで、自動化が困難であることに由来する。オプションで自動追記をしてくれないかと探したが、現時点ではそのようなオプションは用意されていなかった。
import { Injectable } from '@angular/core';
@Injectable()
export class FooService {
constructor() { }
}
ts
出力側はシンプルだ。
ng g class
ng g class [name]
は class を新規に作成する。
ng g class foo
installing class
create src/app/foo.ts
このコマンドの真価は--spec
オプションにある。
ng g class bar --spec
installing class
create src/app/bar.spec.ts
create src/app/bar.ts
ng g class
においては spec ファイルの自動生成がオフのため、オプションを付与する必要がある(angular-cli.json
の設定を変更することでも設定が可能)。
export class Bar {
}
import {Bar} from './bar';
describe('Bar', () => {
it('should create an instance', () => {
expect(new Bar()).toBeTruthy();
});
});
わずかこれだけだが、この手間すら面倒がる開発者はきっといるはずだ。
ng g interface
ng g interface [name]
は TypeScript のinterface
を新規に作成する。
ng g interface foo
installing interface
create src/app/foo.ts
export interface Foo {
}
複数のコマンドを繋いで一気に生成したい場合などは使えるかもしれないが、単体で使うには結果が寂しい。なお、オプションは定義されていない。
ng g enum
ng g enum [name]
は TypeScript のenum
を新規に作成する。
ng g enum foo
installing enum
create src/app/foo.enum.ts
export enum Foo {
}
TypeScript の enum 自体がいまいち扱いにくいため、個人的にはあまり使うことが無さそうだ。
ng g module
ng g module [name]
はNgModule
を新規に作成する。ECMAScript や Node.js の文脈での module ではない点には気をつけておく。
ng g module foo
installing module
create src/app/foo/foo.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class FooModule { }
あまり使うことはないが、覚えておいて損はない。
以上がng g
による生成をまとめたものだ。
生成オプションのチートシート
ng g
ではオプションが利用できる。実例は先にng g directive
の項にて述べた。それぞれのオプションはangular-cli/packages/angular-cli/blueprints
にて定義されている。該当箇所を下記にまとめる。
--spec
のようにオプションのname
をハイフン 2 つで追記すると有効になる。false
の場合は--flat false
のようにする。型がString
ならばng g component foo --prefix my
のように直接指定すればよい(この場合selector: 'my-foo'
となる)。オプション名は分かりやすく名付けられているので、一つ一つの説明は割愛する。
特筆する事項
スタイルシートの言語選択
CSS ではなく Sass などの言語を利用したい場合はオプションで切り替えが可能である。
ng new sassy-project --style=sass
プロジェクトの途中で切り替えたい場合は設定ファイルを変更する。
ng set defaults.styleExt scss
独自のテンプレートで出力したいとき
あまり推奨しないが、テンプレートを改変することも可能だ。たとえば Component のts
ファイルの書式を改変したければ、以下のパスのファイルを変更すればよい。
./node_modules/angular-cli/blueprints/component/files/__path__/__name__.component.ts
import { Component, OnInit<% if(viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection) { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
@Component({
selector: '<%= selector %>',<% if(inlineTemplate) { %>
template: `
<p>
<%= dasherizedModuleName %> Works!
</p>
`,<% } else { %>
templateUrl: './<%= dasherizedModuleName %>.component.html',<% } if(inlineStyle) { %>
styles: []<% } else { %>
styleUrls: ['./<%= dasherizedModuleName %>.component.<%= styleExt %>']<% } %><% if(viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection) { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= classifiedModuleName %>Component implements OnInit {
constructor() { }
ngOnInit() {
}
}
この 1 行目を次のように変更してみる。
import {Component, OnInit<% if(viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection) { %>, ChangeDetectionStrategy<% }%>} from '@angular/core'
すると出力はこのようになる。
- import { Component, OnInit } from '@angular/core';
+ import {Component, OnInit} from '@angular/core'
「俺にはセミコロンは不要だ」などという過激派は試してみてもいいだろう。くれぐれもチームで扱う場合は合意を取るように。
まとめ
このように、オプションの使い方を覚えると自動生成にも柔軟性が出てくるので、angular-cli
の生成挙動が気に入らない場合も自分好みに対応できることが分かった。AoT コンパイルやテスト環境の構築など手間を省ける利点は多いので、angular-cli
は引き続き推奨していきたい。
それではよいお年を。
Discussion