0

I'm trying to get a better understanding of a new codebase, and haven't found a ton of info (or maybe I'm not fully understanding what I have read) about the performance implications of some choices that were made. The previous maintainer isn't available for insight.

Each controller is set up to make its data request before being rendered initially using this pattern:

$routeProvider.when('/path', { resolve: { someMethod: function($q) { var deferred = $q.defer(); .... do some stuff ... return deferred.promise; } });

This is then injected into the controller - that part makes sense and from my understanding is more performant because you're saving digests by waiting for the data to come through first. A loading state is shown by default and removed when we digest.

The value of the injected param - in this case someMethod - is then assigned to this.someModel.

Throughout the controller, remaining methods and properties are set with this.doStuff = function() { ... }, this.property = 'some string', etc.

In the end, a watch is setup and properties made available to the $scope by setting a $watchCollection like this:

$scope.$watchCollection(angular.bind(this.someModel, function() {
    return this;
}), function(newTitle, oldTitle) {
    if (newTitle.title !== oldTitle.title)
    {
        self.updateThings(newTitle);
    }  
});

I understand that it's binding the context of the watch to the current context of this, then watching to changes on its properties using $watchCollection which will perform shallow reference checks of its top level properties.

Per this article I get that simply using properties in my templates with {{ property }} will cause them to be watched, but in this case they aren't watched until they're bound to the $watchCollection.

Of course this is less expensive than using $watch and passing it true for the optional object equality param, but is it better or worse from a performance perspective than putting things directly onto $scope? Why, specifically?

I've never seen it done this way before, so any readings, insights, or things that could give me a better understanding will be greatly appreciated.

Community
  • 1
  • 1
LouisK
  • 1,126
  • 1
  • 8
  • 20
  • Looks ugly to me, if the object need to be watched it should be part of the scope and then just `$scope.$watchCollection("someModel", ...)`. The perfs will be the same. When you ask `is it better than putting onto $scope` what do you mean exactly? If you mean what I jsut said then perfs are equals. – floribon Apr 27 '15 at 23:54

1 Answers1

1

The only real thing I can think of is that the author wanted to keep everything off the scope directly, only populating properties of the controller instance, which will be available on the scope via the property specified in the controllerAs property on the route definition object.

However, when doing it that way, if you want to set up $watches on the scope to be notified of changes to something, you can't, because you don't actually know what property of the scope it will be published on.

You could settle on a convention, such as vm, ctrl or whatever, for the controllerAs value, but that is not ideal and is fragile and not really a concern of the controller code itself. But if you made that concession, you could use $scope.$watchCollection('vm.someModel', ... instead.

One final possible reason is that $scope.$watch() and $scope.$watchCollection() always ultimately end up dealing with a function as the thing they use to get the value of the thing being watched during the digest. If you pass a string, it uses the $parse service under the covers to turn it into a function. If you pass a function directly, no conversion is necessary, so this can be perceived as a minor performance improvement.

The real reason(s) may be one, all or none of these, but they are valid reasons for adopting this approach. I actually admire it for its purity and strict adherence to "controller as", which is the in-vogue strategy encouraged by the Angular team and community at the moment.

GregL
  • 37,147
  • 8
  • 62
  • 67
  • Thanks Greg! That was great clarification. It's a strange paradigm, but I'll embrace it seeing as it's likely the recommended one. – LouisK Apr 28 '15 at 00:21
  • 1
    Makes sense. But a string is $parsed only once so that won't make any difference. What is still not very clear to me is why using `angular.bind` instead of a simple anonymous `function() { return someModel; }` (yeah you save a closure by creating another one) – floribon Apr 28 '15 at 00:41
  • Yeah, I can't answer your valid question about why `angular.bind()` instead of returning `someModel` directly, sorry. It does seem a bit odd, and the part of this approach that least makes sense. But given how "pure" the rest of the code is, the author was probably reasonably switched on, and so perhaps there is a good reason after all. They should have commented the reason, though. – GregL Apr 28 '15 at 01:05