AngularJS で ViewModel オブジェクトを作り $scope へバインドする、ということをやってみました。これが嬉しいシチュエーションとしては、
- 一画面に複数のUIコンポーネントがある。
- Controller を分ける程でもない。もしくは面倒。
- Factory や Directive 等に分割するのも面倒。
といったところでしょうか。もしくは何も意識していないと Controller が肥大化する傾向があるので、ソースの可読性があがったりとか。ちなみに副作用やパフォーマンスなどは検証していません..
今回の投稿の前提となる AngularJS のバージョンは
1.3.8
です。
実装
完成イメージはこんな感じです。
Controller を書く
ViewModel のメンバーは 変数 name
age
message
とメソッド hello()
とし、その ViewModel を生成する関数 userViewModel
を定義します。
$scope.vm = userViewModel("山田", 34);
で $scope へバインドします。
ViewModel をインスタンス化するメリットが無かったので、コンストラクタ関数にはしていません。してもいいと思います。
'use strict';
angular.module('angularViewmodelDemo')
.controller('MainCtrl', function ($scope) {
var userViewModel = function(name, age){
return {
name: name,
age: age,
message: "",
hello : function(){
this.message = "こんにちは! " + this.name + " さん。";
}
}
};
$scope.vm = userViewModel("山田", 34); //ViewModelを$scopeへバインド
});
View を書く
vm.ほげ
で変数やメソッドにアクセスします。
<div class="container" ng-controller="MainCtrl">
<br>
<p>
名前: {{vm.name}}
</p>
<p>
年齢: {{vm.age}}
</p>
<p>
メッセージ: {{vm.message}}
</p>
<p>
<button ng-click="vm.hello()">挨拶</button>
</p>
<hr>
<p>
新しい名前: <input value="" ng-model="vm.name">
</p>
</div>
冒頭にも書きましたが、一画面に複数のUIコンポーネントがある場合に、構造がわかりやすいかも?
ViewModel をコミット/ロールバックする機能をつけてみる
ここでいう「コミット/ロールバック」は、クライアントサイドでの話です。せっかく ViewModel 形式にしたので、ViewModel ごとバックアップを保持して、ユーザ入力を無しにできる(Undoできる)機能をつけてみます。
Controller
'use strict';
angular.module('angularViewmodelDemo')
.controller('MainCtrl', function ($scope) {
var userViewModel = function(name, age){
+ var _master = {}; //commitされたViewModelを保持するオブジェクト
return {
name: name,
age: age,
message: "",
hello : function(){
this.message = "こんにちは! " + this.name + " さん。";
},
+ rollback: function(){ //元に戻す
+ $scope.vm = angular.copy(_master);
+ },
+ commit: function(){ //保存する
+ _master = angular.copy(this);
+ }
}
};
$scope.vm = userViewModel("山田", 34); //ViewModelを$scopeへバインド
+ $scope.vm.commit(); //まずは初期データを保存する
});
View
<div class="container" ng-controller="MainCtrl">
<br>
<p>
名前: {{vm.name}}
</p>
<p>
年齢: {{vm.age}}
</p>
<p>
メッセージ: {{vm.message}}
</p>
<p>
<button ng-click="vm.hello()">挨拶</button>
</p>
<hr>
<p>
新しい名前: <input value="" ng-model="vm.name">
</p>
+ <hr>
+ <p>
+ <button ng-click="vm.rollback()">ViewModelを戻す</button>
+ <button ng-click="vm.commit()">ViewModelを保存する</button>
+ </p>
</div>
デモはこちら。(GitHubページです。)
⇒ http://hkusu.github.io/angularjs-viewmodel-demo/dist/
もし ViewModel を更新したらサーバサイドのデータも更新する場合は、次のように commit のタイミングでサーバへ POST すれば良いかと。
'use strict';
angular.module('angularViewmodelDemo')
+ .controller('MainCtrl', function ($scope, $http) {
var userViewModel = function(name, age){
var _master = {}; //commitされたViewModelを保持するオブジェクト
return {
name: name,
age: age,
message: "",
hello : function(){
this.message = "こんにちは! " + this.name + " さん。";
},
rollback: function(){ //元に戻す
$scope.vm = angular.copy(_master);
},
commit: function(){ //保存する
_master = angular.copy(this);
+ $http.post('api/users/hoge_id', {
+ name: this.name,
+ age: this.age
+ });
}
}
};
$scope.vm = userViewModel("山田", 34); //ViewModelを$scopeへバインド
$scope.vm.commit(); //まずは初期データを保存する
});
便宜上、上記のように書いてますが、API アクセスは Factory 等に逃した方がよいかと。また実際にはエラー処理も必要です。
おわりに
今回のソースはこちらにおいて置きます。
⇒ https://github.com/hkusu/angularjs-viewmodel-demo