Nick Novotny
Nick Novotny

Reputation: 95

JavaScript function gets called multible times by event listener even though I reference the function

I’m adding an event listener on each time I create an angular controller. Each time I leave this page and come back to it, a new event listener gets added cause the constructor is called again.

When this event gets triggered, the same event gets invoked twice, and if I leave and come back, it gets invoked 3 times… etc.. I only want it to always get invoked once.

Here is the code to add the event listener, and the Listener function it calls: (FYI I'm using TypeScript)

In Constructor:

this.$window.addEventListener("message", this.processApi, false);

Function Called:

processApi = (e) => {
    this.processApiMessage(e.data);
};

I read that I should call a reference to a function instead of typing out the function itself, so both reference the same instance of a function, but the same event listener is being called multiple times.

When I do developer tools in chrome, and go to EventListners, and go to message section, I see a new Window element every time I hit the constructor. I am able to delete each of the EventListers through developer tools but can't seem to get it to work through code when I do:

this.$window.removeEventListener("message", this.processApi, false);

I found out that if I refresh the page, all the event listeners clear and my one in the constructor is created so it works fine.

I'm using angular, and was using the $location service to navigate to the url that hit my controller, so a quick fix was to replace $location.url("url") to window.location.href("url")

This seems to work cause the page gets refreshed when I navigate to it. I would rather keep the $location for routing, and only have my event listener get hit once even though the angular constructor is hit multiple times.

Upvotes: 0

Views: 661

Answers (1)

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28757

You need to remove the event listener when the scope is $destroyed. So, in the controller constructor, you need to inject the $scope object. And in the constructor, do something like this:

$scope.$on('$destroy', () => 
  $window.removeEventListener("message", this.processApi));

To be sure, there are several ways of creating a controller. The most common one (especially if you are using TypeScript) is to create a class for the controller.

I would also consider adding the listener to the rootScope instead of $window, which is more the Angular way of doing things. Putting it all together, it would look like this:

class MyController {
  constructor($rootScope, $scope) {
    'ngInject';

    let unsubscriber = $rootScope.$on('message', this.processApi, false);    
    $scope.$on('$destroy', () => unsubscriber());  
  }
  ...
}

Upvotes: 3

Related Questions