【问题标题】:$watch an object$watch 一个对象
【发布时间】:2013-10-27 15:24:23
【问题描述】:

我想监视字典中的变化,但由于某种原因未调用监视回调。

这是我使用的控制器:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    $scope.$watch('form', function(newVal, oldVal){
        console.log('changed');
    });
}

这里是fiddle

我希望每次更改姓名或姓氏时都会触发 $watch 回调,但它不会发生。

正确的做法是什么?

【问题讨论】:

标签: javascript angularjs angularjs-scope


【解决方案1】:

form 对象没有变化,只有 name 属性发生变化

更新fiddle

function MyController($scope) {
$scope.form = {
    name: 'my name',
}

$scope.changeCount = 0;
$scope.$watch('form.name', function(newVal, oldVal){
    console.log('changed');
    $scope.changeCount++;
});
}

【讨论】:

    【解决方案2】:

    当您正在寻找表单对象的变化时,最好的观察方法是使用
    $watchCollection。请查看官方documentation 了解不同的性能特征。

    【讨论】:

      【解决方案3】:

      true 作为第三个参数调用$watch

      $scope.$watch('form', function(newVal, oldVal){
          console.log('changed');
      }, true);
      

      默认情况下,在 JavaScript 中比较两个复杂对象时,将检查它们的“引用”相等性,这会询问两个对象是否引用同一事物,而不是“值”相等性,它会检查所有对象的值是否相等这些对象的属性是相等的。

      根据Angular documentation,第三个参数是objectEquality

      objectEquality == true时,根据angular.equals函数判断watchExpression的不等式。为了保存对象的值以供以后比较,使用了angular.copy 函数。因此,这意味着观看复杂的对象会对内存和性能产生不利影响。

      【讨论】:

      • 这是一个很好的答案,但我想有时你不想递归地观察一个对象
      • 有。默认情况下,如果您不传递true,则如果监视的值是对象或数组,它将检查引用相等性。在这种情况下,您应该明确指定属性,例如 $scope.$watch('form.name')$scope.$watch('form.surname')
      • 谢谢。这正是我错过的。 Jason,你是对的 - 你并不总是希望看到递归对象,但就我而言,这正是我所需要的。
      • ImmutableJS 和参考比较可能有助于提高性能。
      【解决方案4】:

      对于任何想要查看对象数组中对象更改的人来说,这似乎对我有用(因为此页面上的其他解决方案没有):

      function MyController($scope) {
      
          $scope.array = [
              data1: {
                  name: 'name',
                  surname: 'surname'
              },
              data2: {
                  name: 'name',
                  surname: 'surname'
              },
          ]
      
      
          $scope.$watch(function() {
              return $scope.data,
          function(newVal, oldVal){
              console.log(newVal, oldVal);
          }, true);
      

      【讨论】:

        【解决方案5】:

        性能提示,如果有人有一个带有键 -> 值对的数据存储类型的服务:

        如果您有一个名为 dataStore 的服务,您可以在大数据对象发生更改时更新 时间戳。 这样,您无需深入观察整个对象,而只是观察时间戳的变化。

        app.factory('dataStore', function () {
        
            var store = { data: [], change: [] };
        
            // when storing the data, updating the timestamp
            store.setData = function(key, data){
                store.data[key] = data;
                store.setChange(key);
            }
        
            // get the change to watch
            store.getChange = function(key){
                return store.change[key];
            }
        
            // set the change
            store.setChange = function(key){
                store.change[key] = new Date().getTime();
            }
        
        });
        

        在指令中,您只是在观察时间戳的变化

        app.directive("myDir", function ($scope, dataStore) {
            $scope.dataStore = dataStore;
            $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
                if(newVal !== oldVal && newVal){
                    // Data changed
                }
            });
        });
        

        【讨论】:

        • 我认为值得一提的是,在这种情况下,您将失去访问所存储数据的旧值的功能。在此示例中,newValoldVal 是时间戳值,而不是观察到的对象值。它不会使这个答案无效,我只是认为这是一个值得一提的缺点。
        • 没错,当我想加载一个复杂的数据结构作为某些东西的源(例如一些数据的新版本)时,我主要使用这种方法。如果我关心新旧值,我不会将它们存储在大而复杂的对象中,而是我会拥有我关心的事物的小文本表示,并观察它的变化或在时间戳变化时获取它。
        【解决方案6】:

        你必须改变 $watch ....

        function MyController($scope) {
            $scope.form = {
                name: 'my name',
            }
        
            $scope.$watch('form.name', function(newVal, oldVal){
                console.log('changed');
             
            });
        }
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
        <div ng-app>
            <div ng-controller="MyController">
                <label>Name:</label> <input type="text" ng-model="form.name"/>
                    
                <pre>
                    {{ form }}
                </pre>
            </div>
        </div>

        【讨论】:

        • 用几乎与现有问题完全相同的答案来回答一个两年前的问题?不酷。
        【解决方案7】:

        您的代码不起作用的原因是$watch 默认情况下会进行引用检查。所以简而言之,它确保传递给它的对象是新对象。但是在您的情况下,您只是在修改表单对象的某些属性,而不是创建新的。为了使其工作,您可以将true 作为第三个参数传递。

        $scope.$watch('form', function(newVal, oldVal){
            console.log('invoked');
        }, true);
        

        它会起作用,但您可以使用 $watchCollection,它会比 $watch 更有效,因为$watchCollection 将监视表单对象上的浅层属性。例如

        $scope.$watchCollection('form', function (newVal, oldVal) {
            console.log(newVal, oldVal);
        });
        

        【讨论】:

          【解决方案8】:

          试试这个:

          function MyController($scope) {
              $scope.form = {
                  name: 'my name',
                  surname: 'surname'
              }
          
              function track(newValue, oldValue, scope) {
                  console.log('changed');
              };
          
              $scope.$watch('form.name', track);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-20
            • 1970-01-01
            • 1970-01-01
            • 2016-10-19
            • 1970-01-01
            • 2015-01-06
            • 2016-01-23
            • 1970-01-01
            相关资源
            最近更新 更多