Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to destroy $emit and $broadcast events in AngularJS?

Plnkr example build: http://plnkr.co/edit/gB7MtVOOHH0FBJYa6P8t?p=preview

Following the answer here, I created some $broadcast events to allow actions in the main $scope to close popovers in sub $scopes. However I want to make sure I clean up all my events and not have anything lingering that should not.

I have a popover directive, once the popover is activated I send out:

vs.$emit('popoverOpen');

Then in the main app module ($rootScope), I listen for it here:

vs.$on('popoverOpen',function(events,data) {

    // if 'popoverOpen' is heard, then activate this function
    // which on click $broadcasts out even 'bodyClick'
    // but also destroy the 'popoverOpen' event
    vs.bodyClick = function() {
        $rootScope.$broadcast('bodyClick');
        $rootScope.$$listenerCount.popoverOpen=[];
    };
});

Back in the popover Directive, here is my bodyClick listener:

vs.$on('bodyClick', function() {
    vs.searchPopoverDisplay = false;
    $rootScope.$$listenerCount.bodyClick=[];
});

^ I also have code in there attempting to kill the bodyClick event, however to no avail :(

As you can see below, I've removed bodyClick and popoverOpen from the $$listenerCount (there was never anything in the $$listener object.

However the user is still able to access the vs.bodyClick() function in the main app rootScope, even though popoverOpen should have been removed.


So on first load:

  • If the user clicks around without opening any popover, bodyClick is never captured / transmitted
  • After opening a popover, bodyClick is active
  • If the user clicks on the body, that event is sent out and closes the popover
  • However, now even with NO popovers open, if the user clicks on the body that bodyClick event keeps getting sent out

enter image description here

How would you properly remove the events after the bodyClick event has been sent out to close the popover?


UPDATE I also tried this (https://github.com/toddmotto/angularjs-styleguide#publish-and-subscribe-events), but still the bodyClick event keeps getting sent out after the popover is closed and supposedly destroyed.

popoverDirective function still gets called:

vs.$on('bodyClick', function() {

    console.log('bodyClick received ...');
    console.log($rootScope.$$listener);
    console.log($rootScope.$$listenerCount);

    vs.searchPopoverDisplay = false;

    var unbind = $rootScope.$on('popoverOpen', []);
    vs.$on('$destroy', unbind);
    // $rootScope.$$listenerCount.bodyClick=[];
});

enter image description here

like image 280
Leon Gaban Avatar asked Oct 20 '22 11:10

Leon Gaban


1 Answers

A few notes about your plunkr:

  1. None of your controllers go out of scope, thus none of them emit $destroy.
  2. You're listening for $destroy but attempting to emit destroy, which isn't the same. Remove the $ from the listener and you'll see the callback running.
  3. Because subController is a child of mainController destroying main destroys both and stops the button, along with everything else. This behavior is shown in this plunkr.

So in order to get the pseudo-$destroy to ping, I changed it to listen on just destroy.

Then, instead of calling your unbind, which wouldn't work in this case as we aren't actually $destroying anything, I simply replaced the bodyClick function, as it is bound to the <body>.

$scope.$on('destroy', function() {
  $scope.bodyClick = angular.noop();
});

This seems to have the desired effect. Updated Plunkr here.

Update: I discovered why it was indefinitely adding listeners...! The destroy listener was in the button callback. So each button press would add yet another callback. I moved the destroy listener outside of the popover listener. No more memory leaking!

like image 51
Tony Avatar answered Nov 01 '22 10:11

Tony