Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okunokentaro/9e23e695d75affa8538174cd6de04fad to your computer and use it in GitHub Desktop.
Save okunokentaro/9e23e695d75affa8538174cd6de04fad to your computer and use it in GitHub Desktop.
Angular-CLIチートシート
2016/12/30 にQiitaに投稿した記事のアーカイブです
---
@armorik83です。今回は[`angular-cli`](https://github.com/angular/angular-cli)についてまとめます。
# angular-cli
- https://cli.angular.io/
> 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!を書くところまで](http://qiita.com/armorik83/items/ae737ab584012a0f5876)』というものがあるが、現時点ではこの記事の内容より`angular-cli`によって生成された基礎の方が使いやすい。利点を挙げる。
- Angular公式が良と考えているディレクトリ構成に従える
- [webpack](https://webpack.github.io/)などのバンドラーの環境構築を自動で行うため意識しなくてよい
- `ng serve`による開発中のブラウザ上でのプレビューが高速
- [AoT](https://angular.io/docs/ts/latest/cookbook/aot-compiler.html)を前提としているビルド
- Lintやテストの環境構築が不要ですぐに利用可能
一方で、これらはそのまま欠点にもなりうる。
- 独自のドメインレイヤーが多い場合どこに置けばいいかの指標を`angular-cli`は提示しない
- Angularで完結しない場合、たとえばBabelのトランスパイルを別途挟む必要があるときなど、webpackの設定がブラックボックス化されているため拡張が面倒
- Lintは[TSLint](https://palantir.github.io/tslint/)、テストフレームワークは[Jasmine](https://jasmine.github.io/)に固定される
ただし、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`言語ファイルなどを格納すればいいはず
- `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`を名前に含む必要はない。
```foo-bar-baz.component.ts
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が読み込まれるようになっている。
```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 { 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
```
```foo.pipe.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するか開発者が判断せねばならないためで、自動化が困難であることに由来する。オプションで自動追記をしてくれないかと探したが、現時点ではそのようなオプションは用意されていなかった。
```foo.service.ts
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`の設定を変更することでも設定が可能)。
```bar.ts
export class Bar {
}
```
```bar.spec.ts
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
```
```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
```
```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
```
```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`にて定義されている。該当箇所を下記にまとめる。
- [Component](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/component/index.js#L14-L23)
- [Directive](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/directive/index.js#L14-L19)
- [Pipe](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/pipe/index.js#L14-L18)
- [Service](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/service/index.js#L10-L13)
- [Class](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/class/index.js#L10)
- Interface(定義なし)
- Enum(定義なし)
- [Module](https://github.com/angular/angular-cli/blob/cdf1d08fdb7720afa05bfa837c380d6551fa782c/packages/angular-cli/blueprints/module/index.js#L9-L12)
`--spec`のようにオプションの`name`をハイフン2つで追記すると有効になる。`false`の場合は`--flat false`のようにする。型が`String`ならば`ng g component foo --prefix my`のように直接指定すればよい(この場合`selector: 'my-foo'`となる)。オプション名は分かりやすく名付けられているので、一つ一つの説明は割愛する。
# 特筆する事項
## スタイルシートの言語選択
- https://github.com/angular/angular-cli#global-styles
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'
```
すると出力はこのようになる。
```diff
- import { Component, OnInit } from '@angular/core';
+ import {Component, OnInit} from '@angular/core'
```
「俺にはセミコロンは不要だ」などという過激派は試してみてもいいだろう。くれぐれもチームで扱う場合は合意を取るように。
# まとめ
このように、オプションの使い方を覚えると自動生成にも柔軟性が出てくるので、`angular-cli`の生成挙動が気に入らない場合も自分好みに対応できることが分かった。AoTコンパイルやテスト環境の構築など手間を省ける利点は多いので、`angular-cli`は引き続き推奨していきたい。
それではよいお年を。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment