Как отписаться от трансляции на angularJS. Как удалить функцию, зарегистрированную через $ on

241

Я зарегистрировал свой прослушиватель для события $broadcast, используя функцию $on

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

и я хочу отменить регистрацию этого слушателя на основе определенного бизнес-правила. Но моя проблема в том, что после регистрации я не могу ее зарегистрировать.

Есть ли какой-либо метод в AngularJS для регистрации конкретного слушателя? Такой метод, как $для того, чтобы зарегистрировать это событие, может быть $off. Так что на основе бизнес-логики я могу сказать

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

и эта функция перестает вызываться, когда кто-то транслирует событие onViewUpdated.

Спасибо

ИЗМЕНИТЬ: Я хочу удалить регистратор из другой функции. Не функция, в которой я зарегистрирую ее.

  • 2
    Для всех, кто интересуется, возвращенная функция задокументирована здесь
Теги:

8 ответов

425
Лучший ответ

Вам нужно сохранить возвращенную функцию и вызвать ее для отказа от подписки на событие.

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;
    };
},

Также см. документы.

  • 0
    Да. После отладки кода sorce я обнаружил, что существует массив $$ listeners, который содержит все события, и создал мою функцию $ off. Спасибо
  • 0
    В каком случае вы не можете использовать предоставленный угловой способ отмены регистрации? Разъединение, выполненное в другой области, не связано с областью, которая создала слушателя?
Показать ещё 10 комментариев
47

Глядя на большинство ответов, они кажутся чрезмерно сложными. 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();
});
  • 0
    Так же просто, как здесь, есть много ответов, но это более кратко.
  • 8
    Технически правильно, хотя и немного вводит в заблуждение, потому что $scope.$on On не нужно вручную регистрировать на $destroy . Лучшим примером будет использование $rootScope.$on .
Показать ещё 5 комментариев
26

Этот код работает для меня:

$rootScope.$$listeners.nameOfYourEvent=[];
  • 1
    Глядя на $ rootScope. $$ listeners также является хорошим способом наблюдать жизненный цикл слушателя и экспериментировать с ним.
  • 0
    Выглядит просто и замечательно. Я думаю, что его только что удалили ссылку на функцию. не так ли?
Показать ещё 2 комментария
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');

И вот плункер этого в действии

  • 0
    Работал как шарм! но я немного отредактировал его и положил в раздел .run
  • 0
    Люблю это решение. Делает для намного более чистого решения - так намного легче читать. +1
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);
                    }
                }
            }
        }
});

поэтому, присоединяя мою функцию к корневому корню $, теперь она доступна всем моим контроллерам.

и в моем коде я делаю

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

Спасибо

EDIT: способ AngularJS для этого - в ответе @LiviuT! Но если вы хотите отменить регистрацию слушателя в другой области и в то же время хотите избегать создания локальных переменных, чтобы сохранить ссылки на функцию де-регистрации. Это возможное решение.

  • 1
    Я действительно удаляю свой ответ, потому что ответ @ LiviuT на 100% правильный.
  • 0
    Ответ @blesh LiviuT является правильным и в действительности представляет собой ангулярный подход к отмене регистрации, но он плохо подходит для сценариев, в которых необходимо отменить регистрацию слушателя в другой области. Так что это простая альтернатива.
Показать ещё 2 комментария
1
Ответ на

@LiviuT является удивительным, но, похоже, многие люди задаются вопросом, как повторно получить доступ к функции сбрасывания обработчика из другой области или функции $, если вы хотите уничтожить ее из другого места, кроме того, где она была создана, @Рустем Мусабеков отвечает просто отлично, но не очень идиоматично. (И полагается на то, что должно быть частным элементом реализации, который может меняться в любое время.) И оттуда он просто усложняется...

Я думаю, что легкий ответ здесь заключается в том, чтобы просто передать ссылку на функцию срыва (offCallMeFn в своем примере) в самом обработчике, а затем вызвать ее на основе некоторого условия; возможно, аргумент, который вы указываете в событии, которое вы передаете, или $emit. Таким образом, хендлеры могут срывать себя, когда захотите, где бы вы ни находились, неся вокруг своих собственных разрушений. Например:

// 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 a handler registered at this point.)  

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

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

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

Две мысли:

  • Это отличная формула для обработчика run-once. Просто оставьте условные обозначения и запустите selfDestruct, как только он выполнит свою миссию по самоубийству.
  • Интересно, будет ли исходная область когда-либо правильно уничтожена и собрана мусор, учитывая, что вы несете ссылки на закрытые переменные. Вы должны использовать миллион из них, чтобы даже проблема с памятью, но мне любопытно. Если у кого-нибудь есть понимание, пожалуйста, поделитесь.
0

Один из способов - просто уничтожить слушателя, как только вы закончите с ним.

var removeListener = $scope.$on('navBarRight-ready', function () {
        $rootScope.$broadcast('workerProfile-display', $scope.worker)
        removeListener(); //destroy the listener
    })
0

'$ on' сам возвращает функцию для отмены регистрации

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

вы можете вызвать функцию unregister(), чтобы отменить регистрацию этого слушателя

Ещё вопросы

Сообщество Overcoder
Наверх
Меню