The great thing about coding is that we are able to mix coding style and personal/team preference together. This post is all about a preference of mine that has helped me stick to be principles of readable, efficient, and maintainable code with AngularJS.
It's no secret that I am a fan of the "Controller As" syntax that was revealed in AngularJS 1.2.0, in fact I wrote an entire post about the Controller As feature. I'm also a fan of readable code and keeping things consistent and simple. We tend to spend a lot of time reading code. In fact, I bet you spend much more time reading code than you might imagine ... more-so than writing it.
You'll see this and more in my upcoming Pluralsight course AngularJS Patterns: Clean Code, due out this summer 2014.
vm in the HTML
When I create controllers these principles apply well to the Controller As syntax. This is where I like to apply the variable vm
for my controllers. Here is some typical code I write in HTML
<div ng-controller="Shell as vm">
<h1></h1>
<article ng-controller="Customers as vm">
<h2></h2>
<ul ng-repeat="c in vm.customers">
<li></li>
</ul>
</article>
</div>
Notice I name the controller's instance variable vm
in the HTML. In the MV* world, and specifically in MVVM patterns, VM represents the View's Model (aka ViewModel). But wait, this is MVC right? Meh, it's MV* .. I'm all for patterns, but I don't get hung up on conforming for the sake of conforming. So when accessing the binding scope from the controller I consider it the glue between the HTML View and the JavaScript Controller. This is the View's Model ... to to me, I like calling it vm
. And hey, vm is short, contextual, gets the point across, and is readable.
Can you use anything here? Sure. In fact many examples/demos may have Customers as customers
or CustomersCtrl as customers
. That works fine too, but I like the short Customers as vm
as it tells me instantly that the controller is for Customers and now I know inside my HTML I can use vm
.
Mainstream Controller As
So how would it look if I used something more mainstream? Let's look.
<div ng-controller="Shell as shell">
<h1></h1>
<article ng-controller="Customers as customers">
<h2></h2>
<ul ng-repeat="c in customers.customers">
<li></li>
</ul>
</article>
</div>
Again, this works fine, but now I have customers.customers
in the ng-repeat
. That's ugly and it doesn't convey that it represents controller dot array. Well, what if we call it Customers as custCtrl
or Customers as customerCtrl
? They work too ... and it really comes down to personal preference (of which I am not a fan of these). That's why I broke from the mainstream here.
vm in the JavaScript
Ah, so what does the JavaScript look like? Here I also use vm
to represent the binding scope. This gets rid of the $scope
variable from most of my controllers.
angular.module('app')
.controller('Customers', [function() {
var vm = this;
vm.title = 'Customers';
vm.customers = [
{name: 'Haley'}, {name: 'Ella'}, {name: 'Landon'}, {name: 'John'}
];
}]);
Notice that I use var vm = this;
and then I decorate vm
with the members that should be exposed and data-bindable to to the View. This does 3 things for me.
- Provides a consistent and readable method of creating bindings in my controllers
- Removes any issues of dealing with
this
scoping or binding (i.e. closures in nested functions) - Removes
$scope
from the controller unless I explicitly need it for something else - Makes me happy since its short :)
The name vm
here in the JavaScript does not have to match what is used in the View. So there is no coupling there other than that I like using vm
in both.
One of my favorite reasons for using Controller As is that I no longer have $scope hanging around and the temptation to abuse it as a catch-all and do-everything $scope object are removed. For example, if $scope is always there as a dependency it is easier to add watches, apply, digest, on, emit, broadcast and more right in the controller. Now, they may be needed, but often I can find better ways by abstracting those to factories. So if the $scope
is not present as a dependency, I am less tempted.
Nesting Controllers
One interesting aspect of this is when we nest controllers in a View. The HTML may look something like this
<div ng-controller="Shell as vm">
<h1></h1>
<article ng-controller="Customers as vm">
<h2> in </h2>
<ul ng-repeat="c in vm.customers">
<li></li>
</ul>
</article>
</div>
Notice here that the vm
is used in both the Shell and Customers controller's HTML elements. My rule of thumb is to use vm
until it is no longer clear (borrowed from the great Ward Bell). This might be a case for that (up to your own preferences). Both controllers have a title
property which may be difficult to clearly identify. It is possible as shown here using the $parent
syntax, but if you feel this is a bit to much with the vm
here in the HTML there are other options. One of these is to clarify the vm
as shown below.
<div ng-controller="Shell as shellVm">
<h1></h1>
<article ng-controller="Customers as customersVm">
<h2> in </h2>
<ul ng-repeat="c in customersVm.customers">
<li></li>
</ul>
</article>
</div>
Now we see the shellVm
and the customersVm
in the HTML and it is clear if we want the title
property on either how we can get to it.
And remember, the JavaScript doesn't change one bit. We can still use var vm=this;
in the Controller's JavaScript, use this
, or call it anything we want over there.
Choosing a Path
Really this is a team preference that you have to decide upon. As long as you are consistent, there are many ways to skin this cat. I prefer to use vm
(until it is not clear) and in 95% of the cases for me it's worked wonderfully clear. Choose what works for your team and enjoy coding!