[AngularJS] 3. サービス定義≪改訂≫

---
[目次](http://qiita.com/Koizumi-Greenwich/items/3e327e3331b2f728797e)
≪前の記事 2.[モジュール、コントローラー、サービス≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/789276986bc3ff967eaa)
≫次の記事 4.[$watch≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/539ccc4b995305d7ff00)

---
*【改訂】factroy()での定義をやめ、より自由度、応用性の高いservice()での定義に改訂しました。*

AngularJSのMVCにおける「M (Model)」は、 **サービス** と呼ばれます。
[2. モジュール、コントローラ、サービス≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/789276986bc3ff967eaa)では、AngularJS標準のよく使われるサービスをいくつか紹介しました。
今回は、サービスを定義する方法について解説します。

#service()メソッド

サービスの定義は、 *Angularモジュールのservice()メソッド* を用いて行います。

```text:サービスの定義
モジュール.service(サービス名, function(依存サービス, ....) {
    return サービスオブジェクト
});
```

service()メソッドの第1引数はこのサービスの名称になります。文字列で指定します。

第2引数はサービス本体を定義するための関数オブジェクトです。
この関数の引数には、サービス内で利用したい他のサービス（依存サービス）を指定します。
そして最後にサービスの本体となるオブジェクトを返します。

サービスの本体となるオブジェクトは、ハッシュオブジェクトやクラスなどです。
ここでは最も自由度や応用性の高い、クラスによる定義を解説します。


##クラスの定義
JavaScriptでのクラス定義の方法は、[クラスの定義](http://qiita.com/Koizumi-Greenwich/items/7a579c43084286dd38be) および [即時関数でクラス定義](http://qiita.com/Koizumi-Greenwich/items/6f74230f35f2c5b9a46b)を参照してください。サービスクラスの定義もこれに倣って行います。

##サービスクラスの定義
前述の通り、サービスクラスの定義は [クラスの定義](http://qiita.com/Koizumi-Greenwich/items/7a579c43084286dd38be) に倣います。

```text:サービスクラス定義
モジュール.service(サービス名, function(依存サービス, ....) {
    var 変数 = function(仮引数, ...) {

        〜 コンストラクタの処理 〜

    }

    〜 プロパティ、メソッドの定義 〜
    
    return 変数
});
```

以下の例では商品を表すモデルクラスをサービスとして定義しています。
クラスには商品の名前、単価、数量、合計額を持つプロパティがあり、単価と数量から合計額を計算するメソッドを持っています。
*※即時関数を用い、取得したAngularモジュールモジュールオブジェクトを引数に渡しています。*


```js:sample03.html
(function(module) {
    module.service("Product", function(){
        /**
         * コンストラクタ
         */
        var product = function() {
            this.name  = "";
            this.price = 0;
            this.qty   = 0;
            this.total = 0;
        }
        var p = product.prototype;

        /**
         * 合計を計算する
         */
        p.calcTotal = function() {
            this.total = this.price * this.qty;
        }

        return product;
    });
})(angular.module("sampleModule"));
```

#サービスの利用
定義したサービスを利用するには、コントローラや他のサービスを定義する際に *依存サービス* として指定する必要があります。
また今回の例ではサービスをクラスとして定義しているので、インスタンス化して使用することもできます。

```text:サービスをコントローラで使用する
(function(module) {

    module.controller(コントローラ名, function(サービス, ...) {
	    〜 コントローラの処理 〜
	    
	    var 変数 = new サービス();
	});
    
})(モジュールオブジェクト);
```

以下は、前述の *Productサービス* をコントローラで利用する例です。

```js:sample03.html
(function(module) {

    module.controller("sampleController", function($scope, $timeout, Product) {
        // Productクラスのインスタンスを $scope にセットする
        $scope.product = new Product();
    });
})(angular.module("sampleModule"));
```

#サンプルコード
※「計算する」ボタンで使用している *ng-clickディレクティブ* は、clickイベントに応じて行う処理を指定するものです（詳細は [5. イベント処理]() で解説します）。
この例では、ボタンをクリックすると *ProductクラスのcalcTotal()メソッド* が呼び出されます。

```html:sample03.html
<!DOCTYPE html>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
/**
 * アプリケーション モジュール オブジェクトの定義
 */
(function() {
    var module = angular.module("sampleModule", []);
})();

/**
 * 商品モデルクラス定義
 */
(function(module) {
    module.service("Product", function(){
        /**
         * コンストラクタ
         */
        var product = function() {
            this.name  = "";
            this.price = 0;
            this.qty   = 0;
            this.total = 0;
        }
        var p = product.prototype;

        /**
         * 合計を計算する
         */
        p.calcTotal = function() {
            this.total = this.price * this.qty;
        }

        return product;
    });
})(angular.module("sampleModule"));

/**
 * コントローラの定義
 */
(function(module) {
    /**
     * SampleController
     */
    module.controller("sampleController", function($scope, $timeout, Product) {
        // Productクラスのインスタンスを $scope にセットする
        $scope.product = new Product();
    });
})(angular.module("sampleModule"));
</script>

<!-- sampleModule テンプレート -->
<div ng-app="sampleModule">
    <!-- sampleController テンプレート -->
    <div ng-controller="sampleController" ng-cloak>
        <dl>
            <dt>商品名</dt>
            <dd><input type="text" name="name" ng-model="product.name"></dd>
            <dt>単価</dt>
            <dd><input type="number" name="price" ng-model="product.price"> 円</dd>
            <dt>数量</dt>
            <dd><input type="number" name="qty" ng-model="product.qty"> 個</dd>
            <dt>合計 <button ng-click="product.calcTotal()">計算する</button></dt>
            <dd>{{product.total | number}} 円</dd>
        </dl>
    </div>
</div>
```

---
[目次](http://qiita.com/Koizumi-Greenwich/items/3e327e3331b2f728797e)
≪前の記事 2.[モジュール、コントローラー、サービス≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/789276986bc3ff967eaa)
≫次の記事 4.[$watch≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/539ccc4b995305d7ff00)

---