トピック
Angular2 CORE DOCUMENTATIONのGUIDEの翻訳です。
- DOCUMENTATION OVERVIEW - ドキュメント概要 -
- ARCHITECTURE OVERVIEW - 構文概要 -
- DISPLAYING DATA - データ表示 -
- USER INPUT - ユーザー入力 -
- FORMS - フォーム -
- DEPENDENCY INJECTION - 依存性注入 -
- STYLE GUIDE - スタイルガイド -
注意1)ここに掲載されていない項目は、Angular2 CORE DOCUMENTATIONのGUIDEを直接参照してください。
注意2)2016年10月4日時点の翻訳です。翻訳者はTOEICで700点くらいの英語力なので、英訳が間違っている可能性があります。しかもかなり意訳している箇所もあります。もし意訳を通り越して、誤訳になっているような箇所がありましたらご指摘ください。
ARCHITECTURE OVERVIEW - 構文概要 -
Angularアプリケーションの基礎となる構成要素
AngularはHTMLとJavaScript、もしくはJavaScriptをコンパイルする言語(DartやTypeScript)といった、クライアントサイドのアプリケーションを構築するためのフレームワークです。
Angularはいくつかのコアとなるライブラリと、オプション的なライブラリから構成されています。Angularアプリケーションを作るときは、まずAngular化したマークアップによるHTML テンプレートを構成し、それらのテンプレートを管理するコンポーネントクラスを書きます。それからサービスにアプリケーションロジックを追加して、モジュールにコンポーネントとサービスを追加します。
そしてルートモジュールを起動させ、アプリが動き出せば、ブラウザで保持していた状態をあなたが発したユーザーインタラクションに引き継ぐようになります。
もちろん、Angularには抑えるべきことがまだまだあります。それをこれから、このページで詳しく学んでいきましょう。まずは全体像をお見せします。
この構造図は、Angularアプリケーションのもつ8つの主要な構成要素からなっています。
- Modules - モジュール -
- Components - コンポーネント -
- Templates - テンプレート-
- Metadata - メタデータ-
- Data binding - データバインディング-
- Directives - ディレクティブ-
- Services - サービス-
- Dependency injection - 依存性注入-
これらの構成要素を、あなたなりのやり方で学んでみてください。
このページで参照されているコードは、live exampleで利用できます。
Modules - モジュール -
Angularアプリはモジュール(組み立てられるもの)であり、Angularそれ自体もAngular modulesもしくはNgModulesと呼ばれるモジュラリティシステムです。
Angularモジュールはかなり大きなトピックなので、このページではモジュールの紹介に留めておきます。
Angularモジュールのページで、もっと深く掘り下げます。
すべてのAngularアプリは、最低でも一つ、慣習的にAppModule
と名付けられたルートモジュールを持ちます。
小さなアプリケーションであれば、ルートモジュールがアプリ内で唯一のモジュールとなりますが、多くのアプリでは、それ以外にも多くの機能モジュールを持っています。機能モジュールは、それぞれがアプリケーション領域やワークフロー、または性能が密接に関連した機能に注力したコードの集合体となっています。
Angularモジュールは、ルートモジュールであれ機能モジュールであれ、@NgModule
デコレータをもったクラスとなります。
デコレータは、JavaScript のクラスを修飾する機能です。Angularには複数のデコレータが用意されていますが、それらはクラスが何を意味するのか、どんな働きをするのかがわかるように、メタデータをクラスに付随させます。このサイトでデコレータについて学習してください。
NgModule
はデコレータであり、モジュールであることを説明するプロパティをもった、たった一つのメタデータオブジェクトです。最も重要なプロパティを次にあげます。
-
declarations
- このモジュールに属するビュークラスです。Angularはcomponents、directives、pipesという3種類のビュークラスを持ちます。 -
exports
- ほかのモジュールにあるコンポーネントテンプレートの中で、閲覧および使用を可能にするdeclarationsのサブセットです。 -
imports
- exportされたクラスを持ち、このモジュール内で宣言されたコンポーネントテンプレートに必要とされるほかのモジュールです。 -
providers
- オリジナルで作成したサービスを、このモジュールのグローバルコレクションサービスに追加します。これにより、そのサービスはアプリ内のどこからでもアクセスできるようになります。 -
bootstrap
- ルートコンポーネントと呼ばれるメインアプリケーションビューが、他のすべてのアプリケーションビューの基礎となります。bootstrapプロパティは、ルートモジュールでのみ設定するようにしてください。
以下は簡単なルートモジュールの例です。
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 { }
AppComponent
のexport
は、ただexportのやり方を見せるためだけの例なので、実際のところ、この例で使う必要はありません。
他のコンポーネントではルートモジュールをimportする必要がないので、ルートモジュールは何もexportすることがないのです。
ルートモジュールを起動し、アプリケーションを動かしてみてください。開発中にAppModule
を起動させるなら、main.ts
ファイルを次のようにするといいかもしれません。
COPY CODE
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Angular modules vs. JavaScript modules
Angular moduleは、@NgModule
というデコレータをもつクラスであり、Angularの土台となる機能を担っています。
また、JavaScriptはそれ自体が、JavaScriptオブジェクトの集合体を管理するモジュールの仕組みをもっています
これはAngularのモジュールシステムとはまったく異なるものであり、関連性もありません。
JavaScriptは、それぞれのファイルが1つのモジュールとなっており、全てのオブジェクトはそのモジュールをもつファイルの中で定義されています。
JavaScriptモジュールはexport
キーワードを使って、オブジェクトがpublicであることを宣言し、importステートメントを使って他のモジュールでpublicとなっているオブジェクトにアクセスします。
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
export class AppModule { }
Angularには、2つの異なる相互補完的なモジュールシステムがあります。あなたがアプリを作るときは、それらを使ってください。
Angular libraries
AngularはJavaScriptモジュールの集合体を積んでおり、それらをライブラリモジュールとして扱っています。
Angularライブラリの名称は、@angular
という接頭辞で始まります。
Angularライブラリはnpmパッケージマネージャーでインストールされ、JavaScriptのimportステートメントによってimportされます。
例として、@angular/core
からAngularのComponent
デコレータをimportしてみます。
import { Component } from '@angular/core';
また、JavaScriptのimportステートメントを使って、Angular ライブラリからAngular モジュールをimportすることもできます。
import { BrowserModule } from '@angular/platform-browser';
上記の簡易的なルートモジュールの例では、アプリケーションモジュールはBrowserModule
からのデータを必要としています。
そのデータにアクセスするため、次のようにして@NgModule
のメタデータであるimport
にBrowserModule
を加えます。
imports: [ BrowserModule ],
このようにして、AngularとJavaScriptのモジュールシステムは、同時に使用することが可能です。
しかし、それらは「imports」と「exports」という同じ語彙を共有しているので、2つのシステムに対し混乱を招きやすくしています。
頑張ってください。これを理解するためには、時間と経験が必要です。
Angular modulesのページでより深く学習してください。
Components - コンポーネント -
コンポーネントは、ビューと呼ばれる画面部分を管理します。
たとえば、次のような画面がコンポーネントによってコントロールされています。
- ナビゲーションリンクを伴うアプリのルート画面
- heroesのリスト
- heroの編集
ビューへのサポート内容を含んだコンポーネントのアプリケーションロジックを、クラスに定義する必要があります。そのクラスはAPIのプロパティとメソッドに対し、影響を与えるようになります。
たとえば、このHeroListComponent
はサービスから要求されたときにheroesの配列を返すheroes
プロパティを持っています。また、HeroListComponent
は、ユーザーがheroListから1つheroを選んでクリックしたときに、selectedHero
プロパティを設定するselectHero()
メソッドも持っています。
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(private service: HeroService) { }
ngOnInit() {
this.heroes = this.service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
Angularはアプリケーション上のユーザーの動きに合わせて、コンポーネントを作成し、更新し、破棄します。上記で宣言されているngOnInit()
のように、任意のライフサイクルフックを使って、ライフサイクルのその時々にアクションを起こすことができます。
Templates - テンプレート-
コンポーネントのビューを定義するときは、それに対応するテンプレートの定義も必要です。
テンプレートは、コンポーネントのレンダリング方法をHTML形式でAngularに伝えます。
テンプレートは多少の違いを除けば、普通のHTMLと同じように見えます。
HeroListComponent
では、次のようなテンプレートになっています。
<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<ul>
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</li>
</ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
このテンプレートでは、典型的なHTML要素である<h2>
や <p>
を使っていますが、いくつかの違いも含んでいます。*ngFor
、{{hero.name}}
、(click)
、[hero]
、<hero-detail>
のようなコードは、@Angularのテンプレート文法を使っています。
このテンプレートの最後の行では、新しいコンポーネントであるHeroDetailComponent
を表示するために、<hero-detail>
タグというカスタム要素を使っています。
HeroDetailComponent
は、先にとりあげたHeroListComponent
とは異なるコンポーネントです。HeroDetailComponent
(コード出しません)は、HeroListComponent
に表示されていたリストで、ユーザーが選択した特定のheroの情報を表示します。HeroDetailComponent
は、HeroListComponent
の子になります。
<hero-detail>
が、ネイティブなHTML要素とすごく馴染んでいることに注目してください。カスタムコンポーネントは、同じレイアウト上でネイティブなHTMLとシームレスに混在しています。
Metadata - メタデータ-
メタデータはAngularにおけるクラスの処理方法を示します。
HeroListComponent
のコードを振り返ってみてると、HeroListComponent
がただのクラスであることがわかると思います。それがフレームワークであるという証拠も、「Angular」であるということも、その中にはまったく記述されていません。
事実として、HeroListComponent
は本当にただのクラスです。Angularにコンポーネントであると伝えるまでは、コンポーネントではありません。
HeroListComponent
がコンポーネントであるとAngularに伝えるためには、クラスにメタデータをつける必要があります。
TypeScriptでは、デコレータを使ってメタデータをつけることになります。HeroListComponent
のメタデータは、次のようになります。
@Component({
moduleId: module.id,
selector: 'hero-list',
templateUrl: 'hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
ここでは@Component
デコレータが、その下にあるクラスをすぐにコンポーネントクラスであると特定しています。
@Component
デコレータは、Angularがコンポーネントおよびビューを作成し、表示させるために必要な情報として、必須となる構成オブジェクトです。
@Component
で使用可能な、数少ない構成オプションは次の通りです。
-
moduleId
:templateUrl
のようなモジュールに関連するURLのために、ベースとなるアドレス(module.id
)のソースを設定します。 -
selector
: コンポーネントのインスタンスを作成し、挿入することをAngularに伝えるCSSセレクタです。親となるHTMLの中で<hero-list>
タグを見つけるためには、ここで明示しておく必要があります。たとえば、もしアプリ内にあるHTMLが<hero-list></hero-list>
を含んでいた場合、Angularはこのタグの間にHeroListComponent
のビューインスタンスを挿入します。 -
templateUrl
: 上記のような、コンポーネントのHTMLテンプレートを紐づける、モジュールに関連するアドレスです。 -
providers
: コンポーネントが必要とするサービスを使うために、Dependency Injectionプロバイダを列挙します。これはコンポーネントのコンストラクタがHeroService
を要求することをAngularに伝える方法の1つであり、それによってheroesを表示するためのリストを取得できます。
@Component
にあるメタデータは、コンポーネントで明示されている重要な構成要素が、どこで取得できるかをAngularに伝えます。
テンプレート、メタデータ、コンポーネントは連動してビューを描画します。
Angularの振る舞いを決めるために、同じような形をとっている場合、他のメタデータのデコレータを使ってください。
@Injectable
、@Input
、@Output
は、あまり多くはない、よく使われているデコレータです。
構築について総括すると、Angularに役割を伝えるためには、あなたの書いたコードにメタデータを加えなければならない、ということです。
Data binding - データバインディング-
フレームワークがなかった場合、HTMLにデータの値を流し込み、ユーザー操作をアクションと値の更新に変換するといったコントロールを、あなたが担保する必要があります。
pushやpullのようなロジックを自分で書くのはひどく退屈で間違いやすく、どんな熟練したjQueryプログラマーであったとしても、それを読むことは悪夢だと証言するでしょう。
Angularはデータバインディングという、コンポーネント部分とテンプレート部分を同調させるメカニズムをサポートしています。互いの接続方法をAngularに伝えるため、テンプレートHTMLにバインディングマークアップを加えてください。
図が示すように、データバインディングの文法には4つの記法があります。それぞれの記法が、「DOMへ」、「DOMから」、「両方向へ」という方向性を持っています。
HeroListComponent
テンプレートの例では、3つの記法を使っています。
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
-
{{hero.name}}
は、<li>
タグの中で、コンポーネントにあるhero.name
プロパティの値と置き換えて表示されます。 -
[hero]
プロパティバインディングは、親であるHeroListComponent
から、子であるHeroDetailComponent
のhero
プロパティにselectedHero
の値を渡します。 -
(click)
イベントバインディングは、ユーザーがheroの名前をクリックしたときに、コンポーネントにあるselectHero
メソッドを呼び出します。
双方向バインディングは、ngModel
ディレクティブを使うときに、単一記法でプロパティとイベントのバインディングを結びつける重要な4つ目の記法です。
HeroDetailComponent
テンプレートには、次のような例があります。
<input [(ngModel)]="hero.name">
双方向バインディングでは、プロパティバインディングによって、コンポーネントからインプットボックスにデータプロパティの値を渡しています。また、ユーザーが値を変更すると、イベントバインディングによってその情報がコンポーネントへと伝わり、プロパティを最新の値へと再設定します。
Angularはアプリケーションコンポーネントツリーにあるルートから、すべての子コンポーネントを通して、1回のJavaScriptイベントサイクルにつき、すべてのデータバインディングを処理しています。
データバインディングは、テンプレートとコンポーネントが相互に対話するにあたって、重要な役割を担っています。
データバインディングは親と子のコンポーネントが対話するときにも重要です。
Directives - ディレクティブ-
Angularテンプレートは動的なものです。Angularがテンプレートをレンダリングするとき、ディレクティブによって与えられた指示によってDOMを変更します。
ディレクティブはディレクティブのメタデータをもったクラスです。TypeScriptでは、クラスにメタデータを付随させる@Directive
デコレータを使ってください。
コンポーネントは、テンプレートを持ったディレクティブです。@Component
デコレータは、事実、テンプレートに関する機能を使って@Directive
デコレータを拡張したものです。
コンポーネントは技術的にはディレクティブですが、Angularアプリケーションの特徴的かつ中心的な機能であるため、このarchitectural overviewでは、ディレクティブとコンポーネントを区別して扱っています。
それ以外にも、2種類の異なるディレクティブが存在します。構造ディレクティブと、属性ディレクティブです。
それらのディレクティブは、タグ要素の属性や、たまにタグ名称としても使われますが、代入やバインディングの対象となる場合がほとんどです。
構造ディレクティブは、DOMの要素を追加、削除、交換することで、レイアウトを変更します。
次のテンプレートの例では、2つのビルトイン構造ディレクティブを使っています。
<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
-
*ngFor
はheroesリストをまわして、heroごとに1つの<li>
を出力するようAngularに伝えています。 -
*ngIf
は、選択されたheroが存在している場合のみ、HeroDetail
コンポーネントを表示させます。
属性ディレクティブは、表示されている要素の振る舞い、もしくは表現を変更します。テンプレートでは、通常のHTML属性に似ているので、このように呼ばれています。
双方向バインディングを使用する手段として用いられるngModel
ディレクティブは、属性ディレクティブの1例です。ngModel
は、表示している値のプロパティを設定したり、イベントを変更するよう指示したりすることで、表示されている要素の振る舞い(典型的な例としては<input>
)を変更します。
<input [(ngModel)]="hero.name">
それほど多くはないですが、Angularにはレイアウト構造を変更するディレクティブ(例:ngSwitch)や、DOM要素やコンポーネントの様相を修正するディレクティブ(例:ngStyleやngClass)もあります。
もちろん、自分用のディレクティブを書くこともできます。HeroListComponent
のようなコンポーネントは、カスタムディレクティブの1例です。
Services - サービス-
サービスは幅広く対応しているカテゴリーで、アプリケーションに求められる値、関数、機能のすべてを包括しています。
どんなものでも、サービスとなることができます。サービスは規模が小さく、よく使われる目的をもったクラスであることが多いですが、その目的はわかりやすく、よく練られたものであった方がよいでしょう。
サービスの例
- ログサービス
- データサービス
- バグ報告
- 税計算
- アプリケーション環境設定
Angularのサービスに、特別なことは何もありません。
サービスは何も定義されておらず、サービスの元となるクラスも、設置すべき場所も決まっていません。
しかし、サービスは全てのAngularアプリケーションの基礎となっています。それでいうと、コンポーネントはサービスのお得意様、ということになります。
ここで、ブラウザコンソールにログを表示させるサービスクラスの例を示します。
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
次に、起動したプロミスで、herosにアクセスして返すHeroService
をお見せします。HeroService
はLogger serviceともうひとつ、サーバーとの対話というあまりやりたくない仕事をするBackendService
に依存しています。
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
サービスはどこにあっても構いません。
コンポーネントクラスの記述は少なくあるべきです。コンポーネントクラスで、サーバーのデータにアクセスしたり、ユーザー入力のバリデーションを行ったり、コンソールに直接ログを吐かせたりはせず、そういった作業はサービスにやらせてください。
コンポーネントがすべき仕事は、UXを可能たらしめること以外の何者でもなく、ビュー(テンプレートによってレンダリングされるもの)とアプリケーションロジック(モデルの概念としてよく使われるもの)の調整につとめるべきです。
コンポーネントは、データバインディングに使うプロパティとメソッドを持つものがよく、それによって重要なものをすべてサービスに任せることができます。
Angularはこういった原則を強制しません。あなたが3000行の「キッチンシンク」(なんでも入った多機能なものの暗喩)のようなコンポーネントを書いたとしても、とがめることはありません。
Angularは、アプリケーションロジックをサービスに取り入れやすくすることで、上記のような原則に準拠しやすくしたり、Dependency injectionを使って、サービスをコンポーネントで利用できるようにしたりする支援を行います。
Dependency injection - 依存性注入-
Dependency injectionは、クラスが必要とする依存性を完全な形で保ちつつ、クラスから新しいインスタンスをつくる手法です。クラスが依存するものの多くはサービスです。Angularは必要なサービスを持ったまま、新しいコンポーネントを作るためにDependency injectionを利用しています。
Angularでは、コンストラクタにあるパラメーターの型を見ることで、どのサービスが必要とされているか判別することができます。
以下の例では、HeroListComponent
のコンストラクタはHeroService
を要求しています。
constructor(private service: HeroService) { }
Angularでコンポーネントが作られるとき、コンポーネントが要求するサービスのインジェクタが最初に求められます。
インジェクタは、事前に作られたサービスインスタンスのコンテナを保持します。もし要求されたサービスインスタンスがコンテナになかった場合、インジェクタは新しくサービスを作って、Angularに返す前にコンテナに加えます。
要求されたサービスが全て実行され、返されたとき、それらのサービスを引数として受け取ったコンポーネントのコンストラクタを呼び出すことができます。これがDependency injectionです。
HeroService
を注入する過程は、次のようになります。
もしインジェクタがHeroService
を持っていなかった場合、新しいHeroService
の作り方はどのようにしてわかるのでしょうか。
簡単に言ってしまえば、そのインジェクタを持ったHeroService
のプロバイダをあらかじめ登録しておく必要があります。プロバイダはサービス(大抵の場合は、そのサービス自身)を作るか、返す機能を持っています。
プロバイダはモジュール、もしくはコンポーネントに登録することができます。
同じサービスのインスタンスをどこからでも利用できるよう、プロバイダは基本的にルートモジュールに加えるようにしてください。
providers: [
BackendService,
HeroService,
Logger
],
その代り、コンポーネントレベルでは@Component
メタデータにあるproviders
プロパティの中に加えるようにしてください。
@Component({
moduleId: module.id,
selector: 'hero-list',
templateUrl: 'hero-list.component.html',
providers: [ HeroService ]
})
コンポーネントレベルにプロバイダを登録することで、新しく作られたコンポーネントのインスタンスごとに、新しいサービスのインスタンスを取得することができるようになります。
忘れないように、Dependency injectionのポイントを挙げておきます。
- Dependency injectionはAngularフレームワークの中に張り巡らされていて、どこからでも利用てきる。
-
インジェクタが核となるメカニズムとなる。
- インジェクタは作成したサービスインスタンスのコンテナを保持する。
- インジェクタは、プロバイダから新しいサービスのインスタンスを作成できる。
- プロバイダはサービスを作るときのレシピになる。
- インジェクタを持ったプロバイダを登録する。
Wrap up - 要約 -
Angularアプリケーションのもつ8つの主要な構成要素について、基礎的な部分を学習しました。
- Modules - モジュール -
- Components - コンポーネント -
- Templates - テンプレート-
- Metadata - メタデータ-
- Data binding - データバインディング-
- Directives - ディレクティブ-
- Services - サービス-
- Dependency injection - 依存性注入-
これらはAngularアプリケーションにおけるすべての基礎であり、とっかかりとしては十分すぎる内容です。ただし、これであなたが学習すべきことをすべて終えたわけではありません。
ここで簡単に、Angularの主要な機能、サービスのアルファベット順リストを掲載します。
大事なもののほとんどは、次のドキュメントで網羅されています(もしくは近日中に網羅します)。
- Animations: Angularのアニメーションライブラリを使えば、アニメーション技術やCSSの深い知識がなくてもコンポーネントの振る舞いに動きをつけられます。
- Change detection: Change detection(変更検知)のドキュメントでは、Angularがコンポーネントのもつプロパティの値の変化をどのようにして決め、どのタイミングで画面を更新しているかといったトピックを扱います。また、非同期で動いているものを中断し、その変更検知がもつ戦略を実行するゾーンの使用方法についても扱います。
- Events: イベントのドキュメントでは、イベントを発行、購読するメカニズムを使って、コンポーネントやサービスでイベントを発火させる仕組みを扱っています。
- Forms: HTMLベースのバリデーションとdirtyチェックで、複雑なデータ入力のシナリオをサポートします。
- HTTP: HTTPクライアントを使ってサーバーサイドのアクションを引き起こし、データの取得、保存といったやり取りをサーバーと行います。
- Lifecycle hooks: ライフサイクルフックのインターフェースを実行すると、コンポーネントが生成されてから破壊されるまでの存続期間中に、キー入力のタイミングが利用できます。
-
Pipes: 画面上にある値の変化によってUXを変更させるには、テンプレートでパイプを使うとよいです。次の例で、
currency
パイプの書き方を見てみましょう。price | currency:'USD':true
- これによって、"42.33"という値は
$42.33
と表示されます。
- Router: ブラウザを離れることなく、クライアントアプリケーション上でページからページへ遷移できます。
- Testing: Angularのテストプラットフォームを使って、あなたの作ったアプリケーションの部品が、Angularフレームワーク上でうまく機能しているかどうか、ユニットテストテストを行って試すことができます。
Next Step
Displaying Data- データ表示 -