Reputation: 2047
I'm new to Angular, but have a fair amount of experience in coding and am having some trouble getting data binding in views "right".
My approach is broadly:
ControllerAs
syntax as per John Papa's style guide.The confusion I'm having is that not having an explicit $scope
object obfuscates the model in the MVC philosophy. There was a passing comment to this effect in the blog post, but I didn't see any real conclusion on the matter.
The Angular Docs state that the model is "the single point of truth" for data. Perhaps I read to much into this (is it just the "truth" of values between the V and C?) but this suggests that, if you're using ControllerAs
syntax, that the controller properties are the model -- and the "single point of truth".
If that's a given, and you want to abstract all logic into services, then as I see it, you can't get away from one of two pretty ugly alternatives for keeping the controller properties in sync with the service values:
myController.data = service.data
. This works, but is pretty gross, because all the service data is exposed to the view, and isn't the point of these thin controllers to act as a facade to the view? Or to look at it another way, the service has now become a de facto 'fat' controller, full of business logic.$watch
expressions on each of them. This is not ideal because
Are these really the only ways of doing this in Angular 1.3? Or is there something (more elegant) that I've missed?
Edit: Based on the answers and some additional investigations, I've collected my thoughts on the question in this plunkr. It covers 3 ways (IMHO) not to do service binding and two ways that are probably closer to the 'right' approach.
Upvotes: 2
Views: 683
Reputation: 52867
Services return models. Controllers interact with services to populate the view model ($scope). The view interacts with the view model to render the view.
To keep data in-sync, you have a few options available.
Option 1: Introduce a factory or service whose job is to track instances - like a caching service. Any controller that needs an instance for binding to the view can ask for it from the service. This is clean - no need for explicit watch expressions, or non-specific model binding.
Here is an example. Suppose you want to bind to a User object across multiple controllers.
Create a User Service
app.factory('UserService', function() {
return {
getUserById: function(id) {
...
return user;
}
}
});
Create a Cached User Service
app.factory('CachedUserService', function(UserService) {
var cache = [];
return {
getUserById: function(id) {
if (!cache[id])
cache[id] = UserService.getUserById(id);
return cache[id];
}
}
});
Here, we are leveraging singletons (which all factories and services are), and relying on the Caching service to preserve the model reference. The latter point is essential for making sure that view changes are in sync with model changes.
Use the Cached User Service in any controller that requires it:
app.controller('ctrl', function($scope, CachedUserService) {
$scope.user = CachedUserService.getUserById(...);
});
Option 2: Simply store the model that you would like to keep in sync, higher up the $scope chain (i.e. $rootScope)
app.controller('ctrl', function($scope, $rootScope, UserService) {
$rootScope.user = UserService.getUserById(...);
}
Any child scopes are able to bind to the model higher up the scope chain because of prototypical scope inheritance. Take care, however, when you assign or overwrite models on $scope - you may inadvertently break the model binding. To preserve the reference, use angular.copy
instead.
Upvotes: 2