AngularJS's Controller As and the vm Variable

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.

  1. Provides a consistent and readable method of creating bindings in my controllers
  2. Removes any issues of dealing with this scoping or binding (i.e. closures in nested functions)
  3. Removes $scope from the controller unless I explicitly need it for something else
  4. 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!