285

我已经使用 $on 函数将我的侦听器注册到 $broadcast 事件

$scope.$on("onViewUpdated", this.callMe);

我想根据特定的业务规则取消注册这个监听器。但我的问题是,一旦注册,我就无法取消注册。

AngularJS 中是否有任何方法可以取消注册特定的侦听器?像 $on 这样取消注册此事件的方法可能是 $off。所以基于业务逻辑我可以说

 $scope.$off("onViewUpdated", this.callMe);

当有人广播“onViewUpdated”事件时,此函数将停止调用。

谢谢

编辑:我想从另一个函数中注销监听器。不是我注册它的功能。

4

10 回答 10

488

您需要存储返回的函数并调用它以取消订阅事件。

var deregisterListener = $scope.$on("onViewUpdated", callMe);
deregisterListener (); // this will deregister that listener

这可以在源代码中找到:) 至少在 1.0.4 中。我将发布完整的代码,因为它很短

/**
  * @param {string} name Event name to listen on.
  * @param {function(event)} listener Function to call when the event is emitted.
  * @returns {function()} Returns a deregistration function for this listener.
  */
$on: function(name, listener) {
    var namedListeners = this.$$listeners[name];
    if (!namedListeners) {
      this.$$listeners[name] = namedListeners = [];
    }
    namedListeners.push(listener);

    return function() {
      namedListeners[indexOf(namedListeners, listener)] = null;
    };
},

另外,请参阅文档

于 2013-02-15T16:16:41.480 回答
62

查看大多数回复,它们似乎过于复杂。Angular 内置了注销机制。

使用返回的注销函数$on

// Register and get a handle to the listener
var listener = $scope.$on('someMessage', function () {
    $log.log("Message received");
});

// Unregister
$scope.$on('$destroy', function () {
    $log.log("Unregistering listener");
    listener();
});
于 2015-03-31T20:19:28.517 回答
26

这段代码对我有用:

$rootScope.$$listeners.nameOfYourEvent=[];
于 2013-08-21T10:39:30.083 回答
10

编辑:正确的做法是在@LiviuT 的回答中!

您始终可以扩展 Angular 的范围以允许您像这样删除此类侦听器:

//A little hack to add an $off() method to $scopes.
(function () {
  var injector = angular.injector(['ng']),
      rootScope = injector.get('$rootScope');
      rootScope.constructor.prototype.$off = function(eventName, fn) {
        if(this.$$listeners) {
          var eventArr = this.$$listeners[eventName];
          if(eventArr) {
            for(var i = 0; i < eventArr.length; i++) {
              if(eventArr[i] === fn) {
                eventArr.splice(i, 1);
              }
            }
          }
        }
      }
}());

以下是它的工作原理:

  function myEvent() {
    alert('test');
  }
  $scope.$on('test', myEvent);
  $scope.$broadcast('test');
  $scope.$off('test', myEvent);
  $scope.$broadcast('test');

这是它的一个小动作

于 2013-02-15T16:13:47.190 回答
7

调试代码后,我创建了自己的函数,就像“blesh”的答案一样。所以这就是我所做的

MyModule = angular.module('FIT', [])
.run(function ($rootScope) {
        // Custom $off function to un-register the listener.
        $rootScope.$off = function (name, listener) {
            var namedListeners = this.$$listeners[name];
            if (namedListeners) {
                // Loop through the array of named listeners and remove them from the array.
                for (var i = 0; i < namedListeners.length; i++) {
                    if (namedListeners[i] === listener) {
                        return namedListeners.splice(i, 1);
                    }
                }
            }
        }
});

因此,通过将我的函数附加到 $rootscope 现在它可供我的所有控制器使用。

在我的代码中我正在做

$scope.$off("onViewUpdated", callMe);

谢谢

编辑: AngularJS 的方法在@LiviuT 的回答中!但是,如果您想在另一个范围内取消注册侦听器,同时又想远离创建局部变量以保留取消注册函数的引用。这是一个可能的解决方案。

于 2013-02-15T16:24:40.327 回答
1

@LiviuT 的答案很棒,但似乎让很多人想知道如何从另一个 $scope 或函数重新访问处理程序的拆卸函数,如果你想从创建它的地方以外的地方销毁它。@Рустем Мусабеков 的回答效果很好,但不是很地道。(并且依赖于应该是私有的实现细节,它可能随时改变。)从那里开始,它变得更加复杂......

我认为这里的简单答案是简单地在处理程序本身中携带对拆卸函数(offCallMeFn在他的示例中)的引用,然后根据某些条件调用它;可能是您在 $broadcast 或 $emit 事件中包含的 arg。因此,处理程序可以随时随地拆除自己,随身携带毁灭自己的种子。像这样:

// Creation of our handler:
var tearDownFunc = $rootScope.$on('demo-event', function(event, booleanParam) {
    var selfDestruct = tearDownFunc;
    if (booleanParam === false) {
        console.log('This is the routine handler here. I can do your normal handling-type stuff.')
    }
    if (booleanParam === true) {
        console.log("5... 4... 3... 2... 1...")
        selfDestruct();
    }
});

// These two functions are purely for demonstration
window.trigger = function(booleanArg) {
    $scope.$emit('demo-event', booleanArg);
}
window.check = function() {
    // shows us where Angular is stashing our handlers, while they exist
    console.log($rootScope.$$listeners['demo-event'])
};

// Interactive Demo:

>> trigger(false);
// "This is the routine handler here. I can do your normal handling-type stuff."

>> check();
// [function] (So, there's a handler registered at this point.)  

>> trigger(true);
// "5... 4... 3... 2... 1..."

>> check();
// [null] (No more handler.)

>> trigger(false);
// undefined (He's dead, Jim.)

两个想法:

  1. 对于一次性处理程序来说,这是一个很好的公式。只要放弃条件并在selfDestruct它完成自杀任务后立即运行。
  2. 考虑到您携带对封闭变量的引用,我想知道原始范围是否会被正确销毁和垃圾收集。您必须使用其中的一百万个才能让它成为内存问题,但我很好奇。如果有人有任何见解,请分享。
于 2014-01-08T04:16:39.340 回答
1

注册一个钩子以在组件被删除时取消订阅您的侦听器:

$scope.$on('$destroy', function () {
   delete $rootScope.$$listeners["youreventname"];
});  
于 2019-03-11T21:30:27.097 回答
1

如果您需要多次打开和关闭监听器,您可以创建一个带boolean参数的函数

function switchListen(_switch) {
    if (_switch) {
      $scope.$on("onViewUpdated", this.callMe);
    } else {
      $rootScope.$$listeners.onViewUpdated = [];
    }
}
于 2019-04-11T08:06:21.157 回答
0

'$on' 本身返回注销函数

 var unregister=  $rootScope.$on('$stateChangeStart',
            function(event, toState, toParams, fromState, fromParams, options) { 
                alert('state changing'); 
            });

您可以调用 unregister() 函数来取消注册该侦听器

于 2016-06-13T08:04:22.110 回答
0

一种方法是在完成后简单地销毁侦听器。

var removeListener = $scope.$on('navBarRight-ready', function () {
        $rootScope.$broadcast('workerProfile-display', $scope.worker)
        removeListener(); //destroy the listener
    })
于 2017-10-19T11:51:29.107 回答