【问题标题】:How to stop $observe in AngularJS如何在 AngularJS 中停止 $observe
【发布时间】:2014-01-04 20:41:55
【问题描述】:

我最近在 Angular 的指令中遇到了一些问题,即 jQuery 插件没有被正确清理,因此造成了内存泄漏。

所以,今天,当我进行测试以确保不会发生这种情况时,我意识到没有办法停止观察。

var stopObserving = attrs.$observe('myProperty', function(newValue) {          
  updateElement(newValue);
});

因为我认为它在 $watch 上的工作方式相同,但显然不是。根据文档,$observe 将返回回调函数,即第二个参数。

我有这个测试:

describe('destroy',function(){
    beforeEach(function(){
      $scope.$destroy();
    });

    it('should have emptied the DOM node', function(){
      expect(element.text()).toBe('');
    });
    it('shouldn\'t have any more watchers', function(){
      dump(element.data().$scope.$$watchers);
      expect(element.data().$scope.$$watchers.length).toBe(0);
    });
}); 

它失败了,因为只有一个观察者。我已经检查并调用了$destroy,因此清洁完成了。但是,我怎样才能摆脱那个观察者呢?

代码在这里,如果你好奇的话:

https://github.com/firstandthird/angular-popbox

【问题讨论】:

    标签: javascript angularjs angularjs-directive


    【解决方案1】:

    AngularJS 1.3 及以上版本

    在 Angular 1.3 及更高版本中,$observe returns the deregister function,因此取消注册 $observe 的工作方式与 $watch 完全相同:

    var stopObserving = attrs.$observe(...);
    stopObserving();
    

    AngularJS 1.2 及以下

    在 AngularJs 1.2 中,无法取消注册观察者,而且,正如您正确指出的,$observe 返回回调函数。

    然而,目前有一个 PR 开放更改 $observe 以返回类似于 $watch 和 $on 的注销函数,不幸的是,由于重大更改,这仅针对 1.3 版本进行分类。公关结束here:https://github.com/angular/angular.js/pull/5609

    好消息是,根据PR,实现更改需要完整的 3 行新代码,因此如果您不能等到 1.3 版本,您可以自己轻松实现。

    【讨论】:

    • 那么,我可以从$$watchers 数组中删除该函数吗?还是listeners一些无法访问的代码?
    • 在您的测试中,您确定要在 beforeEach 中销毁的 $scope 与 element.data().$scope 相同吗?你检查过dump($scope.$$watchers)expect($scope.$$watchers.length).toBe(0) 吗?
    • 是的,我很确定,因为那是 scope 我传递给 $compile 以创建指令元素。就像你之前建议的那样,但由于它不起作用,我想确保并改变它......我再次尝试以防万一我忘记并且它不起作用。
    • 如果没有看到您的更多代码,我无能为力。我建议您创建一个 plunker 来隔离问题并在您的问题中分享它,没有它我认为您不会得到太多帮助。
    • 我已经添加了一个指向 Github 存储库的链接,因为它正在测试中。
    【解决方案2】:

    当您调用 $watch() 方法来创建绑定时,AngularJS 会返回一个“取消注册”函数。然后可以使用此函数取消绑定您的 $watch() 侦听器 - 您所要做的就是调用此返回的函数,您的 $watch() 侦听器将被删除。 要查看实际情况,请查看以下代码。在这个演示中,我们正在观察链接收到的点击次数。而且,如果该数字超过 5,我们将显示一条消息;但是,一旦显示消息,我们将删除侦听器,因为它不再具有任何价值。

    访问:http://plnkr.co/edit/nciFRm9HTL3i8xYSUQJa?p=preview

    如您所见,我们存储了 $watch() 语句返回的函数引用;然后,一旦 $watch() 触发了几次,我们调用存储的方法,解除 $watch() 监听器的绑定。如果您查看控制台日志,您可以看到 console.log() 语句在调用“注销”函数后立即停止。

    【讨论】:

    • 这和$observe有什么关系?
    • $observe 和 $watch 做同样的事情。主要是,$watch 依赖于 $parse(即它接受有效的 AngularJS 表达式的字符串,例如 "firstName + ' ' + lastName" ) 并倾向于关注范围内的值,而 $observe 使用 $interpolate(即它接受包含 {{}} 花括号的字符串,例如“{{firstName}} {{lastName}}”),倾向于关注属性值,并且在使用 attrs.$set 时也会立即触发。
    • 我的问题的全部目的是说明,尽管它们是相同的,但在调用返回的函数时它们不会做同样的事情。
    【解决方案3】:

    作为临时解决方案,您可以这样做:

    var key = 'myAttr';
        attrs.$observe(key, function(newValue) {
        updateElement(newValue);
        delete attrs.$$observers[key];
    });
    

    或者如果你想变得非常漂亮:

    attrs.$observe = function(key, fn) {
        attrs.$observe(key, fn);
    
        return function() {
           delete attrs.$$observers[key];   
        };
    }
    
    var unobserve = attrs.$observe('myAttr', function(newValue){
        updateElement(newValue);
        unobserve();
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-14
      • 2018-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多