LoginSignup
10
10

More than 5 years have passed since last update.

AngularJS で ViewModel を定義するという設計パターン

Last updated at Posted at 2015-01-05

AngularJS で ViewModel オブジェクトを作り $scope へバインドする、ということをやってみました。これが嬉しいシチュエーションとしては、

  • 一画面に複数のUIコンポーネントがある。
  • Controller を分ける程でもない。もしくは面倒。
    • Factory や Directive 等に分割するのも面倒。

といったところでしょうか。もしくは何も意識していないと Controller が肥大化する傾向があるので、ソースの可読性があがったりとか。ちなみに副作用やパフォーマンスなどは検証していません..

今回の投稿の前提となる AngularJS のバージョンは 1.3.8 です。

実装

完成イメージはこんな感じです。

スクリーンショット 2015-01-05 16.56.41.png

Controller を書く

ViewModel のメンバーは 変数 name age message とメソッド hello() とし、その ViewModel を生成する関数 userViewModel を定義します。
$scope.vm = userViewModel("山田", 34); で $scope へバインドします。

ViewModel をインスタンス化するメリットが無かったので、コンストラクタ関数にはしていません。してもいいと思います。

Controller
'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.ほげ で変数やメソッドにアクセスします。

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>
</div>

冒頭にも書きましたが、一画面に複数のUIコンポーネントがある場合に、構造がわかりやすいかも?

ViewModel をコミット/ロールバックする機能をつけてみる

ここでいう「コミット/ロールバック」は、クライアントサイドでの話です。せっかく ViewModel 形式にしたので、ViewModel ごとバックアップを保持して、ユーザ入力を無しにできる(Undoできる)機能をつけてみます。

Controller

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

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

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10