53

Inside one of my Angular controllers, I have this:

// controller A
$rootScope.$on("myEventFire", function(event, reload) {
    someAction();
});

In another controller I have this:

// controller B
$scope.openList = function(page) {
    $rootScope.$broadcast('myEventFire', 1);
}

Now, this is a single-page app. When I go to controller A initially and try triggering this event, someAction() is going to be executed once. If I navigate away and come back again to controller A and do the same thing, someAction() gets executed twice. If I do it again, it happens three times and so on. What am I doing wrong here?

misaizdaleka
  • 1,776
  • 3
  • 21
  • 32

5 Answers5

102

Can you try just using $scope.$on()? Every time controller A is created, it is adding a new listener on the root scope, and that doesn't get destroyed when you navigate away and back. If you do it on the controller's local scope, the listener should get removed when you navigate away and your scope gets destroyed.

// controller A
$scope.$on("myEventFire", function(event, reload) {
    someAction();
});

$broadcast sends the event downward to all child scopes so it should be picked up on your local scope. $emit works the other way bubbling up towards the root scope.

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
  • 12
    There are many things in life that I'm grateful for...this answer is one of them. – Scott Sword Oct 23 '15 at 20:36
  • 2
    Wow. Just wow. Thanks for this wonderful answer. Banging my head against a brick wall for the past 45 minutes trying to understand why my $rootScope.$on... method was being called more times than it should. Thank you. – Capt. Rochefort Dec 17 '16 at 06:50
  • Thank you so much for info. Much helpful to understand terminology behind it. – Pragnesh Chaudhari Jan 11 '17 at 18:53
  • was helpful even in 2018. – Beny Apr 23 '18 at 08:38
  • What about if we want to communicate between two controllers? I am using $rootScope.$broadcast('event:login-failed', 401); but its triggered more than one time. – Sabreena Nov 01 '18 at 09:38
27

you can also remove it when the scope is destroyed, by running the return callback from the $rootScope.$on().

I.e.

var destroyFoo;

destroyFoo = $rootScope.$on('foo', function() {});

$scope.$on('$destroy', function() {
  destroyFoo(); // remove listener.
});       
Hanna
  • 10,315
  • 11
  • 56
  • 89
jaf0
  • 1,148
  • 10
  • 20
9

if you don't want to destroy,

I think we can check the listener event first - AngularJS 1.2.15

Check listener event - example

So I think this should work :

if(!$rootScope.$$listenerCount['myEventFire']){
    $rootScope.$on("myEventFire", function(event, reload) {
        someAction();
    });
}
vanduc1102
  • 5,769
  • 1
  • 46
  • 43
  • 1
    it's generally bad form to access $$properties. They are considered private to angular. – jaf0 Aug 02 '16 at 04:29
  • 1
    Worked for me... but is $$listenerCount instead of $$listerCount. – Arthur Menezes Jan 11 '17 at 10:27
  • If your context is static, that should work, but I think you should really figure out where the second listener is getting added because it wouldn't be called. For instance if a control is created and the listener is added, then it is destroyed and another control is created in it's place, the first listener will still be the one that is executed. It's context will still be accessing data in the context of the old control. – Jason Goemaat Jun 15 '19 at 01:12
3

If it helps anyone I had a similar issue in a directive. Each time the directive opened the number of times the event was fired increased.

I solved it by using the following (this was in my controllers init function but I guess it could have been defined in the controller itself. In my case my controller needed to reinitialise when the event fired)

 if (!$scope.onMyEvent) $scope.onMyEvent= $scope.$on('myEvent',function(event,data){
        .....
        });
Richard Turner
  • 423
  • 4
  • 12
1

For my case...

if ($rootScope.$$listenerCount['myEventFire']) {
    $rootScope.$$listeners.broadcastShowMessageError = [];
};

$rootScope.$on('myEventFire', function (event, reload) {
    someAction();
})
Arthur Menezes
  • 241
  • 5
  • 16