[AngularJS] 4. $watch ≪改訂≫

---
[目次](http://qiita.com/Koizumi-Greenwich/items/3e327e3331b2f728797e)
≪前の記事 3.[サービス定義≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/4464986399d3462e6ef7)
≫次の記事 5.[イベント処理≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/12a65385177ebbb488d5)

---
*【改訂】3. サービス定義 の改訂に合わせて改訂しました。*

#$scopeの値の変化によるイベント
AngularJSでは、[$scope（コントローラとビューの間を取り持つオブジェクト）](http://qiita.com/Koizumi-Greenwich/items/789276986bc3ff967eaa#scope)の値の変化を常に監視しています。
そして、値が変化するとイベントが発生します。
このイベントに対応する処理を定義するのが **$watch** メソッドです。

##$watch
\$watch は \$scope オブジェクト内のメソッドです。

```text:$watch書式
$scope.$watch(イベント発生元キー, function(newValue, oldValue, scope) {
    〜 イベントに応じて行う処理 〜
});
```

1つ目の引数 *イベント発生元キー* は、\$scopeのどの項目が変化したときにこのイベント処理を行うか、を決める \$scope 内の項目キーです。

例えば…

- $scope.hoge の値が変化したときに行うイベント処理の場合

```text:$watch例1
$scope.$watch("hoge", function(newValue, oldValue) {
```

- $scope.hoge.foo の値が変化したときに行うイベント処理の場合

```text:$watch例2
$scope.$watch("hoge.foo", function(newValue, oldValue) {
```

…となります。

2つ目の引数は、1つめの引数で指定された項目値が変化したときに呼び出されるイベント処理関数オブジェクトです。

イベント処理関数は3つの引数を受け取ることができます。
第1引数 newValue は、指定された項目の変化後の値です。
第2引数 oldValue は、変化前の値です。
第3引数 scope は、イベントが発生したスコープ・オブジェクト（スコープ全体）です。
これらの引数は必要が無ければ受け取らなくても構いません。

以下は、[サービス定義](http://qiita.com/Koizumi-Greenwich/items/4464986399d3462e6ef7) のサンプルコードを元に、商品の価格と数量が変化したときに合計額を自動的に再計算する \$watch 処理をコントローラに追加するものです。

```js:sample04.html
/* コントローラー内 */
// product.price 変化イベント
$scope.$watch("product.price", function(newValue, oldValue, scope) {
    // product.price に変化があれば、Product.calcTotal() を呼び、product.total を再計算する
    scope.product.calcTotal();
});
// product.qty 変化イベント
$scope.$watch("product.qty", function(newValue, oldValue, scope) {
    // product.qty に変化があれば、Product.calcTotal() を呼び、product.total を再計算する
    scope.product.calcTotal();
});

```

2つの \$watch処理を定義しています。
1つ目は \$scope.product.price が、2つ目は \$scope.product.qty が変化したときに行う処理を定義しています。
この例ではどちらも全く同じ処理、すなわち Product の calcTotal() を呼び出して合計額を計算しています。

#$watchGroup

\$watch メソッドでは1つの項目に対してしかイベント処理を定義できません。
一方、\$watchGroup メソッドでは複数の項目に対して同一のイベント処理を定義することができます。

```text:$watchGroup書式
$scope.$watch([イベント発生元キー, ...], function(newValue, oldValue) {
    〜 イベントに応じて行う処理 〜
});
```

\$watch との違いは、イベント発生元の項目キーを配列で複数指定できる点です。
前述のサンプルを \$watchGroup で定義すると以下のようになります。

```js:sample04.html
/* コントローラー内 */
// product.price, product.gty 変化イベント
$scope.$watchGroup(["product.price", "product.qty"], function(newValue, orldValue, scope) {
    // product.price, product.gtyに変化があれば、合計額を計算する
    scope.product.calcTotal();
});
```

#サンプルコード
単価と数量のフィールドに何らかの値を入力すると、合計が自動的に変化します。

```html:sample04.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();
        
        /*
         * $scope の値の変化を監視する
         */
        // product.price, product.gty 変化イベント
        $scope.$watchGroup(["product.price", "product.qty"], function(newValue, orldValue, scope) {
            // product.price, product.gtyに変化があれば、合計額を計算する
            scope.product.calcTotal();
        });
    });
})(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>合計</dt>
            <dd>{{product.total | number}} 円</dd>
        </dl>
    </div>
</div>
```

---
[目次](http://qiita.com/Koizumi-Greenwich/items/3e327e3331b2f728797e)
≪前の記事 3.[サービス定義≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/4464986399d3462e6ef7)
≫次の記事 5.[イベント処理≪改訂≫](http://qiita.com/Koizumi-Greenwich/items/12a65385177ebbb488d5)

---