szimek
szimek

Reputation: 6484

Changing route in Angular causes re-evaluation of functions on the previous route's controller

I've a got a route (e.g. /items) and its controller has

$scope.pathForItem = function (item) {...}

method, which is used in the template to display path for each item in a list. When I'm on this page and I click any link to change the route, Angular calls $rootScope.$apply() somewhere in its code and that causes re-evaluation of some of functions defined on $scope. The problem is that this re-evaluates functions on scope of the previous route's controller as well, thus calling pathForItem again once for every item on the page. Usually it's not a big issue - it's unnecessary, but doesn't cause any errors.

However, it causes errors when I try to sign out. pathForItem assumes that user object exists (which it gets from authService) and it usually works, because we ensure that user exists in this route's resolve function. Now, when one tries to sign out, Angular calls $rootScope.$apply() and tries to call pathForItem for all items again. However, somewhere in the middle, the user gets finally removed, authService.currentUser() starts returning null and all further calls to pathForItem raise an error...

Should it really work like that? Should Angular's $rootScope.$apply call functions for the controller of the previous route as well? Is there some generic workaround or do I have to make sure that all functions in all controllers that are also used in templates, always check if all objects they use are actually there, even though I've already checked it in resolve function?

I'm using Angular 1.3.10 with ngRoute.

Upvotes: 0

Views: 107

Answers (1)

a better oliver
a better oliver

Reputation: 26828

A function in an (interpolation-)expression is called during every digest cycle and you can't possibly know in advance when this will happen. So your function has to be prepared to be called not only more often then you expect but also at unexpected times.

The culprit in your case is the html5 mode. There is a global event handler that rewrites links and calls $apply. The problem is that it's done even if html5 mode is not enabled. I consider this a bug because the docs say

When html5Mode is enabled, enables/disables url rewriting for relative links

Anyway, you can turn this behavior off in a config block:

$locationProvider.html5Mode({rewriteLinks: false});

See the docs for more information. If you need this behavior then you either prepare your function for your situation or make $scope.pathForItem a value. I recommend the latter one anyway.

Upvotes: 1

Related Questions