AngularJSのサービスの概要とfactory、service、providerの使い分けについてまとめました。
デモとコードサンプルはこちら。
※目次をクリックすると目次の下部にコンテンツが表示されます。
・AngularJSのサービスは、アプリケーション全体で利用する”状態”や”振る舞い”を保持することが出来る関数やオプジェクト。
何度も利用される”振る舞い”、共有する”状態”、キャッシュなどをサービスを使って実現出来る。
・サービスは一度だけインスタンス化され、アプリケーション内でコントローラなどがサービスにアクセスする際は、どれも同一のサービスインスタンスにアクセスする事が出来る。
・サービスがインスタンス化されるのはそのサービスに依存するコントローラなどがロードされるタイミングで、使用されない場合はロードされず、リソースを無駄に使用しない。
・サービスは、factory、service、providorメソッドを使って実装できる。
・コントローラも”状態”を維持できるが、アプリケーション内で何度も生成されたり、消失することもできる。そしてコントローラは、直接他のコントローラと”状態”や”振る舞い”をやり取りすることが出来ない。
2)コントローラとサービスの使い分けの目安
・コントローラはプレゼンテーションロジック、サービスはビジネスロジック。
・コントローラはビューとリンク、サービスはビューと独立。
・コントローラはUI、サービスはアプリ。
・コントローラは都度定義して利用、サービスは繰り返し利用。
・コントローラはユーザーに対してどのデータを取得し、どのデータを表示し、どのようにユーザー操作を処理、スタイリングして表示するか、などを担当。
サービスはサーバー呼出し、共通バリデーションロジック、アプリ全体のデータストア、繰り返し利用するビジネスロジックなどを担当。
3)サービスを利用する方法
コントローラなどからサービスを利用する場合は、コントローラを定義する際に、依存するサービスのサービス名を引数に指定する必要がある。
例)コントローラでAngularJSのビルトインサービスである$log、$windowサービスを利用する場合
構文1)
myModule.controller(“MyCtrl”, [“$log”, “$window”, function($log,$window) {}]);
構文2)
myModule.controller(“MyCtrl”, function($log,$window) {});
1)factoryメソッド
・providorメソッドの簡易版。
・関数、オプジェクトでサービスオブジェクトをリターン。
コントローラなどはfactoryメソッドによってリターンされたサービスオブジェクトにアクセスしてサービスの機能を利用する。
●factoryメソッドを使ったサービス定義例
demoApp.factory('LogService', function() { var logs = []; var logCount = 0; var logLevel = "Error"; return { lists: function() { return logs; }, add: function(msg) { logs.push({ id: ++logCount, msg: "[" + logLevel + "]" + msg }); } }; });
2)serviceメソッド
・JavaScriptのクラス、オブジェクト指向を使って定義。
※個人的には、JavaScpriptで使用されているプロトタイプ継承はJavaなどの言語で使用されているクラスベースの継承と異なっていてあまり使用した事が無く理解できていません。
・ファクトリ関数によってリターンされたオブジェクトをコンストラクタとして使用し、newキーワードを使ってサービスオブジェクトを生成しているようです。
●serviceメソッドを使ったサービス定義例
var Logger = function () { this.logs = []; this.logCount = 0; this.lists = function() { return this.logs; }; this.add = function(msg) { this.logs.push({ id: ++this.logCount, msg: "[" + this.logLevel + "]" + msg }); }; }; var errorLogger = function () { }; errorLogger.prototype = new Logger(); errorLogger.prototype.logLevel = "Error"; angular.module('notesApp', []) .service("LogService", errorLogger)
3)providerメソッド
・サービスオブジェクトをリターンする$getメソッドを定義し、その$getメソッドを含むproviderオブジェクトをリターンする。
・サービスが利用される際、AngularJSはproviderオブジェクトを取得するためにファクトリメソッドをコールし、さらに$getメソッドをコールしてサービスオブジェクトを取得する。
$getメソッドの中身は、factoryメソッドで定義したファクトリ関数に相当する。factoryメソッドでは$getメソッドのみが定義されてそれ以外は空のproviderメソッドが裏で実行されているようです。
・providerオブジェクトにサービスオブジェクトの設定値を制御する関数を追加し、アプリケーション開始前にサービスの設定を行う事が出来る。(通常はAngularJSモジュールのconfigメソッドを使用)
・providorオブジェクトを使ってサービスの設定をする場合は、依存性の指定でサービス名に”Providde”という文字を付与して指定する。
●providerメソッドを使ったサービス定義例
demoApp.provider("LogService", function () { var logLevel = "Error"; return { debugEnabled: function(chk) { if (chk) { logLevel = "Debug"; } return logLevel; }, $get: function () { var logs = []; var logCount = 0; return { lists: function() { return logs; }, add: function(msg) { logs.push({ id: ++logCount, msg: "[" + logLevel + "]" + msg }); } } } } });
●providerオブジェクトを使ってサービスの設定をする例
demoApp.config(function(LogServiceProvider) { LogServiceProvider.debugEnabled(true); });
二つのサブコントローラでログ記録を共有している例です。コントローラのみの場合とサービスを使った場合の例を示しています。
サービスを使った場合は、デモではproviderメソッドを使用したコードで実行していますが、service、factoryメソッドを使って定義しても同様に動作します。