How can I unregister a broadcast event to rootscope in AngularJS?

Angularjs

Angularjs Problem Overview


I have the following:

angular.module('test')
    .controller('QuestionsStatusController1',
    ['$rootScope', '$scope', '$resource', '$state',
    function ($rootScope, $scope, $resource, $state) {

        $scope.action2 = function() {
            $rootScope.$broadcast('action2@QuestionStatusController1');
        }

    }]);

angular.module('test')
   .controller('QuestionsStatusController2',
   ['$rootScope', '$scope', '$resource', '$state',
   function ($rootScope, $scope, $resource, $state) {

    $rootScope.$on('action2@QuestionStatusController1', function {
         //write your listener here
    })

   }]);

It's my understanding that I need to unregister the listening event. Can someone tell me how I could code / do this?

Angularjs Solutions


Solution 1 - Angularjs

If you don't un-register the event, you will get a memory leak, as the function you pass to $on will not get cleaned up (as a reference to it still exists). More importantly, any variables that function references in its scope will also be leaked. This will cause your function to get called multiple times if your controller gets created/destroyed multiple times in an application. Fortunately, AngularJS provides a couple of useful methods to avoid memory leaks and unwanted behavior:

  • The $on method returns a function which can be called to un-register the event listener. You will want to save your de-register function as a variable for later use: var cleanUpFunc = $scope.$on('yourevent', ...); See the documentation for $on: http://docs.angularjs.org/api/ng.$rootScope.Scope#$on

  • Whenever a scope gets cleaned up in Angular (i.e. a controller gets destroyed) a $destroy event is fired on that scope. You can register to $scope's $destroy event and call your cleanUpFunc from that.

You can tie these two helpful things together to clean up your subscriptions properly. I put together an example of this: http://plnkr.co/edit/HGK9W0VJGip6fhYQQBCg?p=preview. If you comment out the line cleanUpFunc(); and then hit the toggle and do stuff button a few times, you will notice that our event handler gets called multiple times, which is not really desired.

Now, after all of that, to make your specific situation behave correctly, just change your code in QuestionsStatusController2 to the following:

angular.module('test')
   .controller('QuestionsStatusController2',
   ['$rootScope', '$scope', '$resource', '$state',
   function ($rootScope, $scope, $resource, $state) {

    var cleanUpFunc = $rootScope.$on('action2@QuestionStatusController1', function {
         //write your listener here
    });

    $scope.$on('$destroy', function() {
        cleanUpFunc();
    });

   }]);

By calling cleanUpFunc() in $destroy, your event listener for the action2@QuestionStatusController1 event will be un-subscribed and you will no longer be leaking memory when your controller gets cleaned up.

Solution 2 - Angularjs

Register the listener on the local $scope, not the $rootScope, and the listener will be destroyed automatically when the controller is removed.

So to publish

// EXAMPLE PUBLISHER
angular.module('test').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {
 
  $rootScope.$broadcast('topic', 'message');

}]);

And subscribe

// EXAMPLE SUBSCRIBER
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {

  $scope.$on('topic', function (event, arg) { 
    $scope.receiver = 'got your ' + arg;
  });

}]);

Plunker

Solution 3 - Angularjs

Here is the source code about the deregistration logic. You can do:

$rootScope.$on('action2@QuestionStatusController1', function () {
    $rootScope.$$listeners['action2@QuestionStatusController1'] = [];
})

or call the deregistration function returned from $on()

var deregistration = $rootScope.$on('action2@QuestionStatusController1', function () {
    deregistration();
})

Solution 4 - Angularjs

$scope.$on('saveCancelLeadInfo', function (event, args) {

        if ($scope.$$listenerCount["saveCancelLeadInfo"] > 1) {

            $scope.$$listenerCount["saveCancelLeadInfo"] = 0;

        } });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1943020View Question on Stackoverflow
Solution 1 - AngularjsPolaris878View Answer on Stackoverflow
Solution 2 - AngularjsposhestView Answer on Stackoverflow
Solution 3 - Angularjszs2020View Answer on Stackoverflow
Solution 4 - AngularjsShaik SubhaniView Answer on Stackoverflow