Sunday, June 22, 2014

$locationChangeSuccess not fired in service

angular.module("app", [
).factory("TrackerUtil", function($rootScope, $location){
    var visited = [];
    $rootScope.$on('$locationChangeSuccess', function (event, toState, fromState){
        visited.push(toState);
    });
    return {
        getVisited: function(){
            return visited;
        }
    };

}).controller("trackedListCtrl", function($scope, ..., TrackerUtil){
    ...
    $scope.visited = TrackerUtil.getVisited();
    ...
})

So you've got code that looks something like this. You're hoping every time the location changes (somebody clicks a link, etc), visited.push() will get called. But for some reason, your service behaves very erratically - fires only sometimes, fires only for certain views, etc. So what went wrong?

When TrackerUtil is registered as an angular service, it doesn't really do much, that is unless you are actually referencing it somewhere in your code like from a controller or from another service. Only if this is the case does angular actually go execute the service body - in our case, registering a listener for the $locationChangeSuccess event. If the service isn't used anywhere, angular doesn't even try checking for it's dependencies.

In my case, I was calling TrackerUtil.getVisited() only from trackedListCtrl and hence had added TrackerUtil as a dependency only for that controller. Because of this, visited was always empty until a view handled by trackedListCtrl was activated, giving the appearance of erratic behaviour.

Solution?
I added it to the app's run() section. Not a great solution, but it works.