In order to make different components to talk to each other, I'm using the $emit
method within a method of a service
Service code:
this.triggerSelectedAction = function( actionName )
{
$rootScope.$emit('action-changed', { actionName: actionName } );
};
this.triggerSelectedProject = function( projectName )
{
$rootScope.$emit('project-changed', { projectName: projectName } );
};
this.triggerSelectedFile = function( fileName )
{
$rootScope.$emit('file-changed', { fileName: fileName } );
};
this.subscribe = function( scope, cb )
{
var eventHandler;
eventHandler = $rootScope.$on('action-changed', cb);
eventHandler = $rootScope.$on('project-changed', cb);
eventHandler = $rootScope.$on('file-changed', cb);
scope.$on( '$destroy', eventHandler );
};
Now let's assume that from somewhere inside the app I'm calling the myService.triggerSelectedAction('doSomething');
and there is a specific $state
(e.g toolsState) loaded at the time whose its $onInit
method waits for this event to occur.
toolsState Controller $onInit
code:
myService.subscribe($scope, function eventOccured(event, data) {
if ( event.name === "action-changed" )
setEnvironment();
});
function setEnvironment() {
// Attach an event listener to the select element
document
.getElementById("design-select")
.addEventListener("change", projectSelectedEvent);
}
Now let's say that we change a state and go for example to the manageState which is also waiting for the same event to occur and has a similar $onInit
code except the fact that it binds the eventListener
to a different element.
manageState Controller $onInit
code:
myService.subscribe($scope, function eventOccured(event, data) {
if ( event.name === "action-changed" )
setEnvironment();
});
function setEnvironment() {
// Attach an event listener to the select element
document
.getElementById("manage-select")
.addEventListener("change", projectSelectedEvent);
}
In such a case, if the event is fired, everything works as expected except that I'm getting a TypeError: Cannot read property 'addEventListener' of null
in the console that refers to the toolsState
Controller line with the addEventListener method. It complains that it cannot find the element with id
of design-select
due to the state change.
To me, it seems that the $scope
of the toolsState
when the state changed and so its myService.subscribe()
method still running.
Is there any way to get rid of this error.
Whenever code passes a function or object to an API, one risks creating object references which will prevent garbage collection. This can result in memory leaks.
The $on
method returns a function that can used to unbind the event handler:
this.subscribe = function( scope, cb )
{
var deRegister = {};
deRegister.ActionChanged = $rootScope.$on('action-changed', cb);
deRegister.ProjectChanged = $rootScope.$on('project-changed', cb);
deRegister.FileChanged = $rootScope.$on('file-changed', cb);
scope.$on( '$destroy', function() {
deRegister.ActionChanged();
deRegister.ProjectChanged();
deRegister.FileChanged();
deRegister = null; //release deRegister object
scope = null; //release scope reference
cb = null; //release callback function reference
));
};
In addition to de-registering the event handlers, the code needs to release any object references created.