後付けの機能を提供するサービス
ここまでのサンプルでは前回/今回を通して、Webページの機能をすべてコンポーネントに直接実装してきました。しかし、実際の開発では、以下のように実装をコンポーネントから分離しておきたい機能もあります。
- ログ出力のように、全体で使いまわす機能
- ビジネスロジックのように、画面との独立性を高めておきたい機能
このような「独立した特定の機能」の実装を、Angular 2ではサービスと呼びます。サービスの実体はTypeScriptのクラスです。例として、デバッグログを出力するサービスのクラスをリスト3に示します。
// サービスを指定するためのデコレーター ...(1)
import { Injectable } from "@angular/core";
// ログ出力を行うクラス ...(2)
@Injectable()
export class Logger {
// デバッグログ出力(Android風)...(3)
d(tag: any, msg: any) {
console.debug("【" + tag + "】: " + msg);
}
}
(1)で参照しているInjectableは、サービスであること(後述の依存性注入で処理できること)を表すデコレーターで、(2)のようにクラス定義の冒頭に「@Injectable()」と記述して指定します。ログ出力の処理は(3)です。このログ出力クラスをサービスとして利用する方法を以下で説明します。
参考 @Injectableを省略できる場合
実はリスト3の実装から@Injectableデコレーターを省略しても、サービスとして問題なく動作します。これはLoggerサービスに他のサービスのインスタンスを受け取るコンストラクターが存在しないためです。後述するリスト6のように、コンストラクターで他のサービスのインスタンスを受け取る場合は、@Injectableデコレーターの指定が必須になります。Angular 2のドキュメントでは、他のサービスの利用有無に関係なく、サービスクラスには必ず@Injectableデコレーターを付加するよう推奨しています。
TypeScriptクラスをサービスにする依存性注入(1)
Angular 2では依存性注入(Dependency Injection)を利用して、TypeScriptクラスのインスタンスを、サービスとしてコンポーネントに与えることができます。インスタンスの生成はAngular 2のフレームワーク側で行うため、利用する側ではインスタンスを生成する必要はありません。この依存性注入でサービスをコンポーネントに与える方法を以下で説明していきます。
その1:Webページ全体で利用するサービス
例として、リスト3のログ出力クラスを用いて、図2のようなデバッグログ出力処理を実装します。一般にログ出力は実装のさまざまな場所で行うので、Webページ実装のどこからでも利用できるように指定します。
まず、ルートモジュールの定義ファイル(app.module.ts)にサービスを登録します。具体的な記述はリスト4のようになります。
//...(略)...
// サービスにするクラスの参照 ...(1)
import { Logger } from "./logger.service";
@NgModule({
imports: [ BrowserModule, PhoneUtilModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ],
providers: [ Logger ] // Loggerクラスをサービスとして登録 ...(2)
})
//...(略)...
まず(1)のimportで、リスト3の内容が記述されたlogger.service.tsからLoggerクラスを参照します。次に@NgModuleデコレータのモジュール定義で、(2)のようにproviders属性を指定します。providers属性は依存性注入でサービスとして提供するクラスの指定で、(2)でLoggerクラスをサービスとして登録しています。providers属性にはカンマ区切りで複数のクラスを指定することもできます。
リスト4のようにモジュール(@NgModuleデコレーター)にサービス登録すると、登録されたサービスはWebページ内のあらゆる場所の実装から利用できるようになります。
Loggerサービスを利用するには、リスト5のように、コンポーネントのコンストラクターにLoggerクラスの引数を指定します。
export class AppComponent {
//...(略)...
/**
* コンストラクター ...(1)
* 依存性注入でloggerオブジェクトを受け取る
*/
constructor(private logger: Logger) {
}
/**
* 選択肢クリック時のイベントハンドラ
*/
onClick(phone: Phone) {
// Loggerサービスで、選択されたスマートフォン名をログ出力 ...(2)
this.logger.d("App", phone.name);
// 選択されたスマートフォン情報を保持
this.selectedPhone = phone;
}
//...(略)...
}
(1)のconstructorメソッドがAppComponentクラスのコンストラクターです。コンストラクターの第1引数にLoggerクラスの変数loggerを指定すると、Angular 2のフレームワークによってLoggerクラスのインスタンスがloggerに設定され、(2)の「this.logger」のようにして利用できます。(2)ではthis.logger.dメソッドで、Loggerクラスのログ出力機能を利用しています。
リスト5を実行して選択肢をクリックすると、onClickメソッド内の(2)の処理により、図2のようにデバッグログが出力されます。
