サービスの登録方法
続いて、実際にサービスを作成していきます。サービスを登録するには表1で示すように5つのメソッドが用意されています。しかし、これらの違いを内部から理解することは難しく、当初はあまり違いに意識せずに利用してみてください。
メソッド名 | 説明 |
---|---|
provider(name,function/object) | 最も多機能で複雑なサービスの登録方法。他の登録方法の基本的実装。 |
factory(name,function) | サービスを作成するための関数を登録。一般的に利用される。 |
service(name,constructor) | サービスを作成するコンストラクタを登録。サービスがクラスの場合にfactoryより簡潔に記述できる。 |
value(name,value/object) | 値やオブジェクトを設定。DIによる他のサービスが利用できない。また、構成フェーズ(サービスの設定)では利用できない。 |
constant(name,value/object) | 値やオブジェクトを設定。DIによる他のサービスが利用できない。valueと異なり、構成フェーズでも利用できる。 |
ビジネスロジックとしてサービスを登録するには、factoryもしくはserviceメソッドを使えば多くの場合には問題がないでしょう。また、単純な値やオブジェクトであるならば、valueメソッドで問題ありません。providerメソッドやconstantメソッドは、サービスを作成する際に設定等が行える構成フェーズというフェーズがありますが、そこで作成するサービスの設定等が行えます。しかし、通常の利用ではこれらのメソッドが不可欠になることはあまりありません。
自作のサービスを登録する
実際のサービスの登録は、リスト1のようにすることができます。
(function(module){ module.factory('Items',function($rootScope){ //(1)サービスの登録 return new app.ArrayItems($rootScope); //(2)実際のサービスの本体 }); // module.service('Items',['$rootScope',app.ArrayItems]); //(3)serviceメソッドを使った場合 }(TodoModule));
factoryメソッドの第一引数には登録するサービス名を指定します(1)。このサービス名は、DI機能(依存性注入)を使ってコントローラや他のサービスで利用します。サンプルアプリケーションで作成するサービスは、$rootScope(ルートスコープの説明は前回を参照)を利用します。続いて、returnで実際に作成したサービスオブジェクトのインスタンスを作成して返します(2)。
また、このようにクラスからのインスタンス作成のみの場合には、(3)のようにserviceメソッドを使っても同じ事ができます。
DIでの依存サービスの指定方法には配列を使った指定方法があります(「DIでの依存サービスの指定方法」を参照)。
今回は、コンストラクタ時に引数が必要なために、この配列を使った記述を指定しています。
コントローラやサービスの作成時に依存するサービスの記述は、これまで紹介してきた(1)の方法とは別に(2)のような方法もあります。
module.factory('Foo',function($http){ .... }); //(1) module.factory('Foo',['$http',function($http){ .... }); //(2)
(2)の記述では、配列を利用して依存するサービスを文字列で指定し、配列の最後にこれまでと同様に処理の記述をする方法があります。これはJavaScriptのミニマイズ処理時で問題を起こさないようにするための対応です。ミニマイズ処理では変数名が短く変更されてしまうこともあるため、引数に指定した変数名がDIの指定として使えなくなります。そのため、依存サービスを文字列として指定できるようになっています。
また、(2)でのfunctionの引数名は、これまでと同じようなわかりやすさのために合わせてありますが動作上は関係がなく、実行時に渡される引数はその前に指定した配列での順番となります。
サービスを作る
続いてサービスの中身を作ります。サービスには大きく分けて2つの機能があります。
ひとつは、詳細を表示する際に一覧からどのタスクが選択されたかを管理するためにコントローラ間でのデータ共有する機能、もうひとつはタスクの取得・追加・削除をするための機能です。これらのメソッドは図4に示すように、6つのメソッドを持ちます。
(function(app){ var ArrayItems = function($scope){ //(1)依存するサービスを引数に指定 this.init($scope); }; var p = ArrayItems.prototype; p.init = function($scope){ //(2)依存するサービスを保存 this.$scope = $scope; this.items = new Array(); this.serial = 0; //(3)各タスクに識別用のシーケンス }; p.setCurrentItem = function(item){ //(4)選択されたアイテムを保存する this.current = item; }; p.getCurrentItem = function(){ //(5)選択されたアイテムを取得する return this.current; }; p.list = function(callback){ //(6)タスクの一覧を取得するメソッド callback.call(this,this.items); }; p.add = function(item, callback){ //(7)タスクを追加するメソッド this.serial++; item.id = "id_" + this.serial; //(8)タスクにIDを付与 var $scope = this.$scope; this.items.push(item); //(9)タスクを保存 // : (省略) }; p.remove = function(item,callback){ //(10)タスクを削除するメソッド var id = item.id; var tmp = new Array(); for(var i = 0; i < this.items.length; i++){ if(item.id != this.items[i].id){ tmp.push(this.items[i]); } } this.items = tmp; //(11)削除したタスクを除いたリストで更新 //(省略) }; app.ArrayItems = ArrayItems; }(this.app));
コンストラクタでは、先ほどのサービスの登録時に指定したサービスを受け取ります(1)。内部での初期化処理であるinitメソッドを使って、thisに対してサービスを登録します(2)。(3)では内部でタスクを管理する為のIDを作成する為のシーケンス番号用の変数を初期化します。このIDは保存時に(8)のように使用します。(4)では選択されたタスクを保存、(5)でその保存されたタスクを取得できるようにします。(6)、(7)、(10)では管理しているタスクデータに対して一覧、追加、削除を行うようにします。
また、実際のデータは(9)のように保存し、(11)で削除します。
作成したサービスを使う
実際のサービスの実装が完了したところで、コントローラ内で利用します。リスト3は、一覧画面のコントローラ部分の実装です。
module.controller('listController',function($scope,Items) { //(1)Itemsを指定するだけ //(2)一覧データを取得する Items.list(function(list){ $scope.items = list; }); //(3)一覧からクリックされた時の処理(詳細ページに遷移) $scope.show = function(item){ Items.setCurrentItem(item); $scope.$parent.changePage('info'); }; //(4)一覧データ変わったときには、再度一覧データを取得し直す $scope.$on('changeItems',function(){ Items.list(function(list){ $scope.items = list; }); }); });
作成したItemsサービスの使用は、(1)のように引数内にItemsを指定するだけで可能です。まず、(2)ではこのコントローラが初めて実行された時に一覧データを取得しておきます。(3)は一覧で各タスクがクリックされた時の処理ですが、setCurrentItemメソッドを使ってクリックされたタスクを覚えておきます。実際に詳細ページに遷移した時にはgetCurrentItemメソッドを使ったそのタスクデータを取得します。このようにして、サービスでは各コントローラ間でデータを共有できます。また、(4)ではItemsサービス側でタスク追加、もしくは削除された時のイベントを取得し、再度一覧データを取得し直しています。