KO是用ko.computed()及subscibe()追蹤ViewModel屬性變動做出反應,在NG中則可透過$scope.$watch()實現,寫法為$watch(觀察對象, 連動函式, 值比對開關)。

觀察對象可以是字串或函式,使用字串時完全比照data-bind="propName"的寫法,也支援運算,例如:"model.firstLame + model.lastName";若使用函式,輸入參數為$scope物件,再依需求傳回要觀察對象屬性或其組合運算內容,例如:function(scope) { return scope.firstName + model.lastName }。

連動函式規格為function(newVal, oldVal, scope),newValue即為前述關察對象字串或函式傳回結果。注意觀察對象函式會在每次$digest()時執行(例如:Scope其他屬性變動或ng-click、$apply()執行時),連動函式則在觀察對象改變時才觸發。另外,NG會一併傳回oldValue及整個Scope物件,以滿足複雜的應用情境。

值比對開關為Boolean,傳入true時,NG會改用angular.equals處理物件比對,新舊值可為不同物件,只要angular.equals()結果相等(物件各屬性都相同)就判為相等。但這種比對方式應用於大型或複雜物件時將耗用較多記憶體跟CPU,尤其在NG Dirty Check機制裡觀察對象字串或函式呼叫機率頗高,要小心對效能的影響。

以下是比照KO範例的簡單示範:

<!DOCTYPE html>
<html ng-app="sampleApp">
<head>
  <meta charset="utf-8">
  <title>Lab 18 - 訂閱屬性變更事件</title>
</head>
<body ng-controller="defaultCtrl">
    <dl>
        <dt>Name</dt>
        <dd><input ng-model="model.name" /></dd>
        <dt>Score</dt>
        <dd><input ng-model="model.score" /></dd>
    </dl>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js"></script>
  <script>
    angular.module("sampleApp", [])
    .controller("defaultCtrl", function($scope) {
      function myViewModel() {
        var self = this;
        self.name = "Jeffrey";
        self.score = 32767;
      }
      var vm = new myViewModel();
      $scope.model = vm;
      //透過$watch關注Scope內屬性的變化
      $scope.$watch("model.name", function(newValue, oldValue) {
        console.log("name=" + newValue);
      });
      $scope.$watch(function(scope) {
        return scope.model.score;
      }, function(newValue, oldValue) {
        console.log("score: " + oldValue + "->" + newValue);
      });
    });
  </script>
</body>
</html>

Live Demo

這裡有個小問題。KO繫結<input>時預設onchange才會觸發重算(但可透過valueUpdate調整),比每按一個鍵就觸發一次連動有效率。但NG 1.2.*預設<input>一改變就連動,到1.3版才有updateOn參數可調,另一種做法是使用debounce設定將改變後延遲一段時間,累積多次變動只執行一次,也能解決問題,如以下示範:

<!DOCTYPE html>
<html ng-app="sampleApp">
<head>
  <meta charset="utf-8">
  <title>Lab 15 - 訂閱屬性變更事件</title>
</head>
<body ng-controller="defaultCtrl">
    <dl>
        <!-- ver 1.3+支援updateOn設定 -->
        <dt>Name</dt>
        <dd><input ng-model="model.name" ng-model-options="{ updateOn: 'blur' }" /></dd>
        <dt>Score</dt>
        <dd><input ng-model="model.score" 
                  ng-model-options="{ updateOn: 'default', debounce: { default: 500 } }" /></dd>
    </dl>
    <div>
      {{model.name}}
    </div>
  <script src="http://code.angularjs.org/1.3.0-beta.10/angular.js"></script>
  <script>
    angular.module("sampleApp", [])
    .controller("defaultCtrl", function($scope) {
      function myViewModel() {
        var self = this;
        self.name = "Jeffrey";
        self.score = 32767;
      }
      var vm = new myViewModel();
      $scope.model = vm;
      //透過$watch關注Scope內屬性的變化
      $scope.$watch("model.name", function(newValue, oldValue) {
        console.log("name=" + newValue);
      });
      $scope.$watch(function(scope) {
         return scope.model.score;
      }, function(newValue, oldValue) {
        console.log("score: " + oldValue + "->" + newValue);
      });
    });
  </script>
</body>
</html>

Live Demo Name欄位要移開焦點才更新,Score欄位則是停頓超過0.5秒就觸發

[NG系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post&t=angularjs

Comments

Be the first to post a comment

Post a comment