Javascript 如何在 AngularJS 中查看作用域上的所有更改?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14545936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 17:09:51  来源:igfitidea点击:

How to watch all changes on a Scope in AngularJS?

javascriptangularjs

提问by Luke Dennis

Angular's $watch function allows events to be fired when the specified attribute changes, as shown below. Is there is a similar way to listen for events when anychange happens on the scope?

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);

回答by bmleite

I believe you cannot watch for changes on the $scope(apart from using @ValentynShybanov's sugestion). The closest thing you can do is to watch for digest calls:

我相信你不能观察$scope(除了使用@ValentynShybanov 的 sugestion)的变化。您可以做的最接近的事情是观察摘要调用:

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

The above function will be called each time a $digestis invoked on the scope, even if there were no changeson the scope's properties.

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

回答by Yaroslav

That is not straightforward, cause the scope is a plain object. That is Backbone where you can subscribe to all events, but for that your models should extend a proprietaryinterface - Backbone.Model. Angular puts aside the observer pattern in it's classical look. That is an unusual design decision. Basically there is a loop where Angular evaluates expressions being watched and when it detects a difference from the previous iteration it notifies watchers. A really interesting approach, probably the only alternative to the observer pattern (except perhaps reactive programming, which is not common yet).

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

I propose to use as a watch expression a function, which returns a hash of the scope. Unfortunately scopes contain circular references somewhere so quick solutions like JSON.stringify($scope)don't work. I wrote quick and dirty proof of concept. http://jsfiddle.net/ADukg/2544. Whatever textfield you type in it prints to the console. String hash function is taken from here Generate a Hash from string in Javascript/jQuery.

我建议使用一个函数作为监视表达式,它返回作用域的哈希值。不幸的是,范围在某处包含循环引用,所以快速解决方案如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");}
    );
}

回答by guy mograbi

I ran into this problem myself, and I solved it with something similar to @ValentynShybanov's comment but not quite the same. my solution does not require you to prefix all properties as you stated in the comment.

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

see a working plunkr

看到一个工作的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);

as you see the solution is quite the same, but in fact it adds another property to the scope rather than replacing the structure you already have. this new property represents the entire scope (hence its name) and you can watch it.

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

the loop I wrote might be too generic, you can replace it with a customized initialization. for example:

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

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

this solution answers your needs and spares you the need to refactor the code.

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

回答by Tejaswi Sharma

A easy solution could be that you should check a variable of the scope only but it could be and object.

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

app.js

应用程序.js

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

});

index.html

索引.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>

The trick here is to pass 'true' in the third parameter in the $watch function which will do a angular.equal check which is check by value rather than a simple reference check. Please let me know if its correct.

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