フロントエンドMVCフレームワーク Angular (所謂Angular4)を触ってみた。
ここでは Angular CLI を使う。

環境構築

Yarnインストール

$ npm install -g yarn
$ ndenv rehash
$ yarn --version

Angular CLIインストール

Angular CLI のインストール。
パッケージ管理ツールを npm から yarn へ変更。

$ yarn global add @angular/cli
$ ndenv rehash
$ ng set --global packageManager=yarn
$ ng version
_                      _                 ____ _     ___
/ \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
/ ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
           |___/
@angular/cli: 1.3.0
node: 8.3.0
os: darwin x64

以降、 ng コマンドでいろいろできるようになる。

プロジェクト作成と起動

$ ng new angular-sample
installing ng
  create .editorconfig
  create README.md
  create src/app/app.component.css
  create src/app/app.component.html
  create src/app/app.component.spec.ts
  create src/app/app.component.ts
  create src/app/app.module.ts
  create src/assets/.gitkeep
  create src/environments/environment.prod.ts
  create src/environments/environment.ts
  create src/favicon.ico
  create src/index.html
  create src/main.ts
  create src/polyfills.ts
  create src/styles.css
  create src/test.ts
  create src/tsconfig.app.json
  create src/tsconfig.spec.json
  create src/typings.d.ts
  create .angular-cli.json
  create e2e/app.e2e-spec.ts
  create e2e/app.po.ts
  create e2e/tsconfig.e2e.json
  create .gitignore
  create karma.conf.js
  create package.json
  create protractor.conf.js
  create tsconfig.json
  create tslint.json
Installing packages for tooling via yarn.
Installed packages for tooling via yarn.
Successfully initialized git.
Project 'angular-sample' successfully created.

yarn で作成されていることを確認する。
また、下記の通りプロジェクトディレクトリに移動して、導入されている Angular のバージョンを確認する。

$ cd angular-sample
$ ng version
_                      _                 ____ _     ___
/ \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
/ ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
           |___/
@angular/cli: 1.3.0
node: 8.3.0
os: darwin x64
@angular/animations: 4.3.4
@angular/common: 4.3.4
@angular/compiler: 4.3.4
@angular/core: 4.3.4
@angular/forms: 4.3.4
@angular/http: 4.3.4
@angular/platform-browser: 4.3.4
@angular/platform-browser-dynamic: 4.3.4
@angular/router: 4.3.4
@angular/cli: 1.3.0
@angular/compiler-cli: 4.3.4
@angular/language-service: 4.3.4

以下で起動。

$ ng serve

プロジェクト構成

.
├── README.md
├── .angular-cli.json                * Angular CLI 設定ファイル
├── .editorconfig                    * エディタの設定ファイル
├── .gitignore
├── e2e                              * エンドツーエンドテストディレクトリ
│   ├── app.e2e-spec.ts
│   ├── app.po.ts
│   └── tsconfig.e2e.json
├── karma.conf.js                    * karma(テストランナー)の設定ファイル
├── package.json                     * npmの設定ファイル
├── protractor.conf.js               * e2eテストの設定ファイル
├── tsconfig.json                    * TypeScriptの設定ファイル
├── tslint.json                      * TypeScriptのLinter設定ファイル
└── 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.app.json             * TypeScriptの設定ファイル
    ├── tsconfig.spec.json            * TypeScriptのテスト設定ファイル
    └── typings.d.ts                  * TypeScriptの型定義ファイル     

.ditorconfigEditorConfigというプロジェクトの設定ファイルで、エディタへプラグインなどを入れることにより様々なエディタの設定をプロジェクト単位で可能にする。
テストは Jasmine (テスティングフレームワーク)と Karma (テストランナー)の組み合わせで実行される。
e2e(エンドツーエンド)テストは protractor

主要なコマンド

  • ng new
    • 新規にAngularプロジェクトを作成する
  • ng build
    • src 配下をビルドして dist へ出力する
  • ng serve
    • Webサーバを起動してアプリケーションを実行する
  • ng generate
    • blueprints から選択して新たにコードを生成する
  • ng eject
    • ng generate などで作成したコードを取り除き、webpackの設定やスクリプトを変更する
  • ng get
    • 設定(key-value)から値(value)を取得する
  • ng set
    • 設定(key-value)に値(value)を追加する
  • ng lint
    • Linterを実行する
  • ng test
    • テストを実行する
  • ng e2e
    • e2eテストを実行する
  • ng xi18n
    • コードからi18nメッセージを抜く

上記以外や詳細なオプションは ng help を参照。

blueprints の種類

  • module
    • ng generate module sample
    • モジュール( sample/sample.module.ts )が作成される。
    • 自動で指定したモジュール名でディレクトリが切られることに注意
  • component
    • ng generate component sample
    • コンポーネント(以下)を作成し、 app.module.ts に自動登録(importおよびメタデータ declarations への追加)される。
      • sample.component.tssample.component.spec.tssample.component.htmlsample.component.scss
    • なお、既に存在するモジュール名で作成すると、そのモジュール名のディレクトリ内に作成される。
  • directive
    • ng generate directive sample
    • ディレクティブ(以下)を作成し、 app.module.ts に自動登録(importおよびメタデータ declarations への追加)される。
      • sample.directive.tssample.directive.spec.ts
  • pipe
    • ng generate pipe sample
    • パイプ(以下)を作成し、 app.module.ts に自動登録(importおよびメタデータ declarations への追加)される。
      • sample.pipe.tssample.pipe.spec.ts
  • service
    • ng generate service sample
    • サービス(以下)を作成する。
      • sample.service.tssample.service.spec.ts
  • class
    • ng generate class sample
    • 普通のクラス( sample.ts )が作成される。
  • interface
    • ng generate interface sample
    • 普通のインターフェース( sample.ts )が作成される。
  • enum
    • ng generate enum sample
    • 普通の列挙型( sample.enum.ts )が作成される。

ディレクトリを切りたい場合は / を加えて、 ng generate service sample/sample-service のようにする。

Angular CLI のバージョンアップ方法

グローバルでは以下。

$ yarn global remove @angular/cli
$ yarn cache clean
$ yarn global add @angular/cli@latest
$ ndenv rehash

ローカルプロジェクトでは以下。

$ rm -rf node_modules dist # use rmdir on Windows
$ yarn add @angular/cli@latest --dev
$ yarn install

Angularの基本

Angularのアーキテクチャは以下の要素から構成される。

  • モジュール(@NgModule)
  • コンポーネント(@Components)
  • テンプレート(Templates)
  • メタデータ(Metadata)
  • データバインディング(Data Binding)
  • ディレクテイブ(@Directive)
  • パイプ(@Pipe)
  • サービス(Services)・DI(Dependency injection / @Injectable)
  • フォームとバリデータ
  • Angularライブラリ

上記の各々について後述する。

モジュール(@NgModule)

Angularのアプリケーションはモジュール単位で機能を管理する。
モジュールは後述の コンポーネントテンプレート を包含する。

Angularアプリケーションは少なくとも1つ ルートモジュール を持ち、AppModule と命名する。
ルートモジュールは、 src/main.ts からロードされる。
通常のアプリケーションの場合、ルートモジュールは1つだが、巨大なアプリケーションの場合、複数のルートモジュールを持つこともある。

モジュールは以下のように作成する。

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({
  imports:      [ BrowserModule ],
  providers:    [ Logger ],
  declarations: [ AppComponent ],
  exports:      [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

AppModule クラスに @NgModule デコレータ (Javaでいうアノテーション)を付与した形になっている。
デコレータにAngularに則した設定を行い、クラスには何も書かない(たぶん)。
@NgModule の代表的なプロパティは以下の通り。ちなみにデコレータに設定するプロパティを メタデータ という。

  • imports
    • このモジュールに他のモジュールを取り込み、このモジュール内で定義されているコンポーネントやテンプレートが他のモジュールのクラスを使用できるようになる。
    • 他のモジュールの providersexports に定義されたものを使用できるようになる。
  • providers
    • このモジュールおよび関係するコンポーネント・サービスへインジェクトするためのサービスクラスを定義・インスタンス化する。
  • declarations
    • このモジュールに所属させるビュークラスを指定する。Angularのビュークラスには、 コンポーネントディレクティブパイプ がある。
  • exports
    • このモジュール内のクラスを他のモジュールのコンポーネント・テンプレートで使用可能にする。
    • imports の逆。
  • bootstrap
    • ルートコンポーネント を定義する。ルートコンポーネントはアプリケーションのメインビュー。
    • ルートモジュールにだけbootstarp プロパティを設定する。

以下が ルートモジュール を指定する方法( src/main.ts )。

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

また、モジュール内では以下のようにライブラリから他のモジュールをロードすることもできる。

import { Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

ロードしたライブラリモジュールは、 @NgModuleimports プロパティに指定することにより使用できるようになる。

imports:      [ BrowserModule ],

コンポーネント(@Component)

コンポーネントはビューの役割を担う。
コンポーネントクラス内で定義したフィールド変数やメソッドは、テンプレートから直接使用できる。
ロジックは基本的にコンポーネント内に記載し、サービスクラスをDIしたりする。

@Component({
  moduleId: module.id,
  selector:    'hero-list',
  templateUrl: './hero-list.component.html',
  providers:  [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}

@Component の代表的なメタデータプロパティは以下。

  • moduleId
    • このモジュールが定義されるファイルのES/CommonJSモジュールID
  • selector
    • このコンポーネントのHTMLタグ名を定義する。
  • template
    • テンプレートを直接記載する。
  • templateUrl
    • テンプレートファイルのパスを指定する。
  • styles
    • スタイルシート(CSSやSass)を直接記載する。このコンポーネント外には影響が無い。
  • styleUrls
    • スタイルシートファイルのパスを指定する。このコンポーネント外には影響が無い。
  • providers
    • このコンポーネントがDI経由で使用するサービスクラスを指定する。
    • コンポーネントクラスの constructor の引数でも指定する必要がある。

その他の @Component のメタデータはここで確認できる。

テンプレート(Templates)

テンプレートはコンポーネントのDOMとして使われるHTML。
コンポーネントクラスの @Component デコレータのメタデータに直接記載( template )することも、別ファイルとして作成して読み込む( templateUrl )ことも可能。
テンプレート内では、HTMLの属性として ディレクティブ*ngFor など)、コンポーネントとのデータ・機能のやりとりに データバインディング を使用することができる。

  • コンポーネントフィールドへのアクセス
    • *ngFor : コレクションフィールドの値にアクセス
    • *ngFor="let hero of heroes" : コレクションheroesの各要素をheroへ代入
  • コンポーネントのメソッド呼び出し
    • (click)="onClickMe()" : クリック時に onClickMe() メソッドを呼び出す
    • (keyup)="onKey($event)" : キーアップ時にイベントを引数に onKey() メソッドを呼び出す

また、テンプレートにはスタイルシート(CSS、Sassなど)を指定することができ、@Component デコレータのメタデータ( stylesstyleUrls )で指定することができる。

メタデータ(Metadata)

メタデータは デコレータ@ から始まるJavaでいうアノテーション)に設定するプロパティ。
Angularにクラスがどのように挙動するか知らせる役割。
@Injectable@Input@Output など様々なデコレータにメタデータを設定できる。
もう十分前述しているのでこの程度で。

データバインディング(Data Binding)

テンプレートとコンポーネント間のデータ・機能のやりとりを行う機能。
以下のように4種類ある。

  • 単方向バインド(interpolation)
    • <li>{{hero.name}}</li>
    • コンポーネントの hero.name プロパティの値を <li> 表示する
  • プロパティバインド(property binding)
    • <hero-detail [hero]="selectedHero"></hero-detail>
    • 親コンポーネントの selectedHero の値を、子コンポーネントの hero へ渡している
  • イベントバインド(event binding)
    • <li (click)="selectHero(hero)"></li>
    • ユーザのクリックにより selectHero() メソッドが呼び出される。
  • 双方向バインド(Two-way data binding)
    • <input [(ngModel)]="hero.name">
    • ngModel を用いて 参照と更新 を両方同時に実現する。

ngModel のようにテンプレートのHTMLタグの属性として記述できるAngularの機能を ディレクティブ という。

イベント

<button (click)="onClickButton()">ボタン1</button>
<button (click)="onClickButtonWithEvent($event)">ボタン2</button>

(click) のように、イベント名をカッコで囲んだ属性に、イベントを処理するメソッドを記述する。
引数なしの記述と、イベントオブジェクト $event を指定する記述ができる。
コンポーネントクラス内では以下のようにメソッド定義する。

onClickButtonWithEvent(event:any) {
   // targetプロパティでイベントを発生させたオブジェクトを取得
   var button = event.target;
   // ボタンのラベルをtextContentプロパティから取得して表示
   alert("ボタン「" + button.textContent + "」が押下されました");
 }

ディレクテイブ(@Directive)

ディレクティブは前述の通り、テンプレートのHTMLタグの属性として指定できるAngularの機能。
モジュール・コンポーネント間のデータの授受や、ループ・条件分岐といった動的なDOMの機能を提供する。

<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>

@Directive デコレータでディレクティブを自作できる。
ここでは割愛。参考

パイプ(@Pipe)

Pipeはテンプレート内で文字列操作の機能を提供する。
例えば、Dateオブジェクトをあるフォーマットに整形して表示できる。

// コンポーネントクラスのフィールド
let birthday = new Date(1985,3,1); // これで1985年4月1日 なことに注意

上記をテンプレードで以下のように指定することにより yy/MM/dd 形式で表示できる。

<p>{{ birthday | date:"yy/MM/dd" }}</p> <!-- 1985/04/01 と表示される -->

他にも下記のようなパイプがある。

  • DatePipe
    • 上記例。日付を整形する。
    • date_expression | date[:format]
  • UpperCasePipe
    • 文字列を大文字にする。
    • expression | uppercase
  • JsonPipe
    • 入力値にJSON.stringfyを実行して返す。
    • expression | json

また、パイプは以下のような感じでチェーンできる。

{{ birthday | date | uppercase}}

さらに、@Pipe デコレータでパイプを自作できる。
ここでは割愛。参考

サービス(Services)・DI(Dependency injection / @Injectable)

サービスは export された通常のtypescriptクラス。
DIを使用してコンポーネントや他サービスクラスにサービスクラスをインジェクトできる。

作成例

$ ng generate service sample/sample
installing service
  create src/app/sample/sample.service.spec.ts
  create src/app/sample/sample.service.ts
  WARNING Service is generated but not provided, it must be provided to be used

上記で作成した src/app/sample/sample.service.ts をDIするには、使用するモジュール・コンポーネントにimport、メタデータ providers およびコンストラクタへの指定を行う。
以下コンポーネントへの導入例。

...
import { SampleService } from './sample/sample.service' // インポート

@Component({
  ...
  providers: [SampleService], // providers に追加
  ...
})
export class AppComponent {
  title: string;

  constructor(
    private sampleService: SampleService // コンストラクタに設定
  ){
    this.title = sampleService.getTitle();
  }
}

DI・インジェクタ

サービスをモジュールやコンポーネントで使用できるようにインジェクトする機能。
前述の通り、モジュール・コンポーネントにimport、メタデータ providers およびコンストラクタへの指定を行う。

@Injectable デコレータがついたサービスクラスは多段Inject(DIのDI)することが可能になる。
一段Injectはデコレータが無しでもOKだがつけておいても問題ない。

フォームとバリデータ

http://codezine.jp/article/detail/9596?p=4

Angularライブラリ

Angularのライブラリには様々用意されており、APIリファレンスはここ
リファレンスではクラスの前に記号がついており、それぞれ以下の意味。

記号 意味
D Directive。ディレクティブ。
P Pipe。パイプ。
@ Decorator。デコレータ。Javaでいう所謂アノテーション。
C Class。普通のTypeScriptのクラス。
I Interface。普通のTypeScriptのインターフェース。
F Function。関数。
E Enum。普通の列挙型。
T Type Alias。なんかわからん。
K Const。定数クラス。

その他話題、メモ

TypeScriptの型解決について

解決方法には以下がある。

  • tsd
  • typings
  • @types

Angularは型定義が含まれているため、上記3つは不要。ただし、他のライブラリを用いる場合 @types を使用する。(typingsに変わったかも。。。)

おすすめ記事