Reputation: 12561
I have slightly modified the example from the following URL (http://docs.angularjs.org/cookbook/helloworld) as follows, placing the name
value within an attrs
object property:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
<script>
function HelloCntl($scope) {
$scope.attrs = {
name : 'World'
}
}
</script>
</head>
<body>
<div ng-controller="HelloCntl">
Your name: <input type="text" ng-model="attrs.name"/>
<hr/>
Hello {{attrs.name || "World"}}!
</div>
</body>
</html>
One benefit I can see is that the HTML source code can be searched for /attrs\.\w+/
(e.g.) if there is ever a need to easily find all such attributes within the view rather than the controller (e.g. a search for name
could collide with form element names). Also within the controller I can only imagine that partitioning attributes necessary for the front end might lend itself to better organization.
Is anybody else using such a level of abstraction. Are there any possible specific further benefits to it's usage? And most importantly, might there be any specific drawbacks to it.
Upvotes: 2
Views: 310
Reputation: 24676
It's recommended that you always use a dot in your ngModel
s in order to avoid potential issues with prototypal inheritance that are discussed in Angular's Guide to Understanding Scopes:
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.
Prototypal inheritance and primitives
In javascripts' approach to inheritance reading and writing to a primitive act differently. When reading, if the primitive doesn't exist on the current scope it tries to find it on any parent scope. However, if you write to a primitive that doesn't exist on the current scope it immediately creates one on that scope.
You can see the problem this can cause in this fiddle that has 3 scopes- one parent and two children that are siblings. First type something in the "parent" and you'll see that both children are updated. Then type something different in one of the children. Now. only that child is updated, because the write caused the child to creates it's own copy of the variable. If you now update the parent again, only the other child will track it. And if you type something into the sibling child all three scopes will now have their own copies.
This can obviously cause lots of issues.
Prototypal inheritance and objects
Try the same experiment with this fiddle in which each ngModel
uses a property of an object instead of a primitive. Now both reading and writing act consistently.
When you write to a property of an object it acts just like reading does (and the opposite of how writing to a primitive does). If the object you're writing to does not exist on the current scope it looks up it's parent chain trying to find that object. If it finds one with that name then it writes to the property on that found object.
So, while in the primitive example we started with 1 variable and then after writing to the children ended up with 3 copies of the variable- when we use an object we only ever have the one property on the one object.
Since we almost always, perhaps just always, want this consistent behavior the recommendation is to only use objects properties, not primitives in an ngModel
or, said more commonly, "always use a dot in your ngModel
"
Upvotes: 2
Reputation: 42736
I do this as well. I also put all action functions (button clicks etc) into a $scope.actions object. And since i use socket.io i put those callbacks into a $scope.events object it usually keeps my controllers nice and organized and easily able to find the function i need to if i need to do any editing.
app.controller('Ctrl',['$scope', function ($scope) {
$scope.data = {
//contains data like arrays,strings,numbers etc
};
$scope.actions = {
//contains callback functions for actions like button clicks, select boxes changed etc
};
$scope.events = {
//contains callback functions for socket.io events
}
]);
Then in like my templates I can do like
<input ng-click="actions.doSomething()">
I also do a partial of this for services. I use a private and public data object
app.factory('$sysMsgService',['$rootScope',function($rootScope){
//data that the outside scope does not need to see.
var privateData = {};
var service = {
data:{
//contains the public data the service needs to keep track of
},
//service functions defined after this
};
return service;
}]);
Upvotes: 2