ngResource は $get してオブジェクトを変更して \$save を呼んだらサーバサイドに反映とかいうことができます。

  1. データを $get などで取得した直後の状態、(サーバサイドで) 保存されている状態
  2. 取得したリソースを変更した状態、(サーバサイドで) 未保存の状態
  3. $save などで保存完了した状態、(サーバサイドで) 保存されている状態

とリソースの状態が変化します。

しかし、実際のアプリケーションでは、変更されたが更新されていない状態というのを表示したくなることが多々あります (変更があって未保存なら項目の色を変えるとか)。

なので、保存前の状態をどこかに保存して、保存されたらそれを更新するとかいう作業が必要になります。ただ、コントローラ側の ngResource のコールバックでいちいちこんなことしようとするとバグるので、ngResource の定義側でなんとかしたいところです。

transformResponse を使う

transformResponse は ngResource で何かしたときに呼ばれてくれるので、ここで angular.copy() で resource.saved みたいなプロパティに変更前のデータを全部つっこんでやると大変楽です。だいたい以下のようなコードです (実際はコードをまとめるけど)。

var Entry = $resource('/api/entries', { id : '@id' }, {
    'query':  {
        method:'GET',
        isArray: true,
        transformResponse : function (data, headers) {
            data = angular.fromJson(data);
            data.saved = angular.copy(data);
            return data;
        }
    },
    'save':  {
        method:'POST',
        transformResponse : function (data, headers) {
            data = angular.fromJson(data);
            data.saved = angular.copy(data);
            return data;
        }
    }
});
// controller
$scope.entry = Entry.get({ id : 1 });
// $scope.entry.saved is original data
// template
<div ng-class="{ changed: entry.body != entry.saved.body }">
    <textarea ng-model="entry.body"></textarea>
</div>

このように、取得したリソースを $scope に突っ込み、リソースを対象に ng-model を設定してやると、あとはいい感じになります。save などを呼ぶと変更された内容でリクエストが飛び (unsaved も送信されるのが余計ですが)、サーバ側で適切に保存されたエントリのレスポンスを返せば、transformResponse でリソースの更新がかかるので、全状態が自動でリセットされます。

  1. トップ
  2. tech
  3. ngResource を使って未保存の情報を明確にする