【问题标题】:How to watch all changes on a Scope in AngularJS?如何在 AngularJS 中查看 Scope 的所有变化?
【发布时间】:2013-01-10 20:11:21
【问题描述】:

Angular 的 $watch 函数允许在指定属性更改时触发事件,如下所示。当范围发生任何变化时,是否有类似的方法来监听事件?

// works
$scope.$watch("someval", function() { }, true);
$scope.$watch(function(scope){ return scope.someval; }, function() { }, true);

// doesn't work
$scope.$watch("this", function() { }, true);
$scope.$watch(function(scope){ return scope; }, function() { }, true);

【问题讨论】:

  • 我建议将所有可以更改的属性放入某个对象中,并使其成为 $scope 的可观察属性。这不是一个解决方案,但它可以工作并且可能会使您的范围更具结构化/可维护性(您可以将该对象传递给另一个函数,而不是整个范围,甚至可能导致内存泄漏。
  • @ValentynShybanov:这是一个好方法,但我必须为每个绑定值添加前缀(“container.someval”等)。是否有一种简单的方法可以从标记中创建子范围? (见相关问题:stackoverflow.com/questions/14546039/…

标签: javascript angularjs


【解决方案1】:

我自己遇到了这个问题,我用类似于@ValentynShybanov 的评论解决了这个问题,但并不完全相同。 我的解决方案不需要您按照评论中的说明为所有属性添加前缀

查看working plunkr

var entireScope = {};
  for ( var i in $scope ){
    if ( i[0] !== '$' && i !== 'this' && i !== 'constructor'){
      console.log(i);
      entireScope[i] = $scope[i];
    }
  }

  $scope.entireScope = entireScope;

  $scope.$watch('entireScope', function( newValue, oldValue ){
   $log.info('entireScope has changed',newValue, oldValue);
  }, true);

如您所见,解决方案完全相同,但实际上它向范围添加了另一个属性,而不是替换您已有的结构。这个新属性代表整个范围(因此得名),您可以观看它。

我写的循环可能太通用了,你可以用自定义的初始化来替换它。例如:

$scope.entireScope = { 'someVal' : $scope.someVal }

此解决方案可满足您的需求,并且无需重构代码。

【讨论】:

    【解决方案2】:

    一个简单的解决方案可能是您应该只检查范围的变量,但它可能是对象。

    app.js

    app.controller('MainController', ['$scope', function($scope){
       $scope.watchable = {
           'param1':'',
           'param2':''
       };
       $scope.update = -1;
       $scope.$watch('watchable', function(scope){
           scope.update++;
       }, true);
    
    });
    

    index.html

    <div ng-controller="MainController">
        <input type='text' ng-model="watchable.param1">
        <input type='text' ng-model="watchable.param2">
        <p>param1:{{watchable.param1}}, param2:{{watchable.param2}}</p>
        <p>update:{{update}}
    </div>
    

    这里的技巧是在 $watch 函数的第三个参数中传递 'true' ,这将执行 angular.equal 检查,这是按值检查而不是简单的引用检查。请让我知道它是否正确。

    【讨论】:

      【解决方案3】:

      这并不简单,因为范围是一个普通对象。那是您可以订阅所有事件的 Backbone,但为此您的模型应该扩展一个 专有 接口 - Backbone.Model。 Angular 在其经典外观中摒弃了观察者模式。这是一个不同寻常的设计决定。基本上有一个循环,Angular 评估被观察的表达式,当它检测到与前一次迭代的差异时,它会通知观察者。一种非常有趣的方法,可能是观察者模式的唯一替代方案(除了可能反应式编程,这还不常见)。

      我建议使用一个函数作为监视表达式,该函数返回范围的哈希值。不幸的是,作用域在某处包含循环引用,所以像JSON.stringify($scope) 这样的快速解决方案不起作用。我写了快速而肮脏的概念证明。 http://jsfiddle.net/ADukg/2544。您在其中输入的任何文本字段都会打印到控制台。字符串哈希函数取自这里Generate a Hash from string in Javascript/jQuery

      function MyCtrl($scope) {
          $scope.$watch(
              function($scope) {return hash($scope);},
              function() {console.log("changed");}
          );
      }
      

      【讨论】:

      • 我应该在计算对象哈希时跳过角度属性。
      • 您可以使用angular.toJson 而不是JSON.stringify 来做到这一点。
      • 我在我的作用域属性前面加上了一个名称,这样计算哈希值就更容易了,没有任何冲突,它的打字稿给出了类似$scope.$watch(() =&gt; angular.toJson($scope.hypothesis,false),() =&gt; { /*do whatever you need*/ });
      • 我认为在作用域上使用 angular.toJson 只会生成字符串 '$SCOPE' 从而使这个解决方案无关紧要。在这种情况下,正确的方法是按照建议进行自定义 JSON.stringify
      【解决方案4】:

      我相信您无法看到$scope 的变化(除了使用@ValentynShybanov 的建议)。您可以做的最接近的事情是观察摘要调用:

      $scope.$watch(function() { 
          console.log("digest called"); 
      });
      

      每次在作用域上调用$digest 时都会调用上述函数,即使作用域的属性没有更改

      【讨论】:

      • $digest()也可以多次调用,做好准备。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-30
      • 1970-01-01
      • 2013-03-01
      • 2015-10-22
      • 2015-07-23
      • 1970-01-01
      • 2013-02-13
      相关资源
      最近更新 更多