AngularJS’s Controller As and the vm Variable | John Papa

John Papa

Evangelist on the loose

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>{{vm.title}}</h1>
  <article ng-controller="Customers as vm">
    <h2>{{vm.title}}</h2>
    <ul ng-repeat="c in vm.customers">
      <li>{{c.name}}</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>{{shell.title}}</h1>
  <article ng-controller="Customers as customers">
    <h2>{{customers.title}}</h2>
    <ul ng-repeat="c in customers.customers">
      <li>{{c.name}}</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>{{vm.title}}</h1>
  <article ng-controller="Customers as vm">
    <h2>{{vm.title}} in {{$parent.vm.title}}</h2>
    <ul ng-repeat="c in vm.customers">
      <li>{{c.name}}</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>{{shellVm.title}}</h1>
  <article ng-controller="Customers as customersVm">
    <h2>{{customersVm.title}} in {{shellVm.title}}</h2>
    <ul ng-repeat="c in customersVm.customers">
      <li>{{c.name}}</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!

tags: angular javascript
  • Ward Bell

    “Good stuff!” says the lesser Ward Bell

    • johnpapa7

      Ward – Wait, there are two of you? Uh oh.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1636

  • http://zud.io/ markrendle

    I was also a user of the “same-name” approach (although in my case I used “ctrl”), until I started using UI-Router and working with several levels of nested views. Then I found that I wanted to declare members on a parent controller and bind to them in nested controllers, at which point having distinct names for each “ctrl” becomes much more useful. So now I default to using distinct names, because it saves refactoring later.

  • http://sampathloku.blogspot.com/ Sampath Lokuge

    Really nice approach John.I would like to maintain the 2nd approach which you have mentioned on Nesting control’s section.Which is more elegant and clear.That is ‘Customers as customersVm’ approach.And I would like to give a same vm name inside the Controller also.It’s like this ‘var customersVm =this;’.So What’s your thoughts about that simple extension ? :)

    • johnpapa7

      Sampath – Thanks. That works too. I prefer vm in the JavaScript since the context is already super clear personally. But that works just fine too.

      • http://sampathloku.blogspot.com/ Sampath Lokuge

        Thanks John.Keep up the good work. :)

  • yngvebn

    While I do agree that the ControllerAs syntax makes for pretty code, I’m not sure I quite agree with your ‘vm’-approach. In your Customer-controller example, the take-away for assigning var vm = this; is that you can get rid of a single .bind(this) on your function, and in my opinion I can live with that binder. var vm = this; reminds me too much about var self = this; in Knockout which I’m really glad I don’t have to write anymore! :)

    I’m thinking that if the controller gets full of functions that require you to call .bind(this), I’d consider moving some of that logic out into other services, to keep my controllers from not growing out of proportion, leaving me with very few functions requiring .bind(this) in my code.

    When it comes to the markup, you say you don’t like for instance “customers.customers” and would rather do “customersVm.customers”, whereas I’m thinking the namingissue is further down. What about renaming the customers-property and call it ‘all’ or something more verbose, so that the markup becomes “ng-repeat=’customer in customers.all’” instead. That enables us to be verbose and declarative in the markup, instead of having to postfix everything with ‘Vm’.

    I really like how you remove redundant “Controller” or “Ctrl” on your Controller-declarations, so why not do the same for variables in the view? We already know that the controller is responsible for providing the view with data and other functionality, so it’s implicit that it’s a “ViewModel” already.

    Just my humble opinions. What do you think?

    • johnpapa7

      yngvebn – Thanks for the feedback. As I said there are many ways to skin this cat. It really is up to you. I’ve stated my case on this vs vm in the JavaScript so I won;t repeat myself, but I will add that using “this” often leads to confusing code within closures. I agree that self=this is not something I prefer, but only because self is not as descriptive in a controller as vm is. I believe the mainstream demos for Controller As use “this” so if that’s your choice, you have good company. I’ve personally found it easy to teach and maintain when using `var vm = this;`

      You could use `customers.all` if the context works well for you; in this case it certainly could. The JavaScript would be `vm.all = [ ... ];` Consistency and clarify/readability are key and I think this is fine.

      Yeah I like removing the redudnant Controller, but consider that its redundant because I say `ng-controller=’Customer as vm` …. I already said the word controller as its in a `ng-controller`. I do like having the `customerVm` or `vm` in the HTML so I am clear that its the `vm` and not a customer. Why? Because when I am in the HTML I am often writing code away from that directive perhaps in `{{customerVm.name}}` or `ng-repeat o in customerVm.orders` .

      However, while I am talking about my preferences here keep in mind your preferences for your team are what is important. Not mine. So if these conventions work for you, roll with it!

  • Angel

    So John, have you dumped Durandal?

  • Joe Hoppe

    I tried to share this on twitter, but I received this error message: Warning: mysql_connect(): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. in D:homesitewwwrootwp-includeswp-db.php on line 1372

    • http://sampathloku.blogspot.com/ Sampath Lokuge

      Me too came that message.Not only from the TW but also from the others .So I tried 2,3 times and then worked.Try and try again,One day you’ll fly. :)

    • johnpapa7

      Thanks. Not sure what’s up. Notified the Azure folks to ask for help.

  • Gustavo Robles

    Nice article, do you know how can I use the parent vm in the children?

    Lets say I have a parent controller that has vm.product, and then in the html I have a nested controller but inside that particular html I need access to vm.product, any thoughts? I did some research and I found something about $scope.$parent.property but, since vm gets rid of $scope, how can I achieve this?

    • johnpapa7

      $parent.vm

  • Keuller Magalhaes

    Hi John, I’d like use this approach because is more clear and simple, but I’d like to know if it works together ngRoute module ? For example, when I define some route like that:

    $routeProvider.when(‘/empresas’, {
    templateUrl: ‘/basico/empresas/view’,
    controller:’EmpresasCtrl’
    });

    I’m specifying my template and controller, in this case I don’t have ng-controller directive, so I cannot define ‘controller as’ syntax.

    • johnpapa7

      use `controllerAs` in the route

  • Pingback: AngularJS's Controller As and the vm Variable |...

  • https://chiefy.github.io/ Chief

    Nice write-up! We’ve actually used Angular Classy (http://davej.github.io/angular-classy/) which helps a ton with controller readability. It also supports “Controller As!”

    • johnpapa7

      looks cool!

  • Pingback: Andrey on .NET | Интересности #40

  • https://www.kidsministryteam.com Brian

    This is great. I definitely like the clarity it brings when using controllers. Do you have any suggestions for isolated scope directives? It is common in our application to have a directive that could benefit from a single root ViewModel for the sake of it’s own template but where we restrict the input / output bindings of the directive to a few primitive types. Basically don’t want to provide the entire VM via a binding, but means we have to copy values and wire up $watch statements to keep properties on the VM in sync with specific bindings.

  • sephie

    I noticed that in you “controllers-with-or-without-sugar” article you used the following style in controllers:

    var vm = this;
    //code
    return vm; // <– look at meee!

    In the current article's 'Customers' controller you're omitting the return statement at the end. Why?

    One of the best explanations and discussion on the new syntaxt that I've read. Thanks!

%d bloggers like this: