Write AngularJS like a pro. Angularjs Icon

Follow the ultimate AngularJS roadmap.

A better way to scope angular.extend no more vm this

The evolution of Angular Controllers has changed over the last year. As of now, most of us are working with the most recent addition to “Controller” syntax with the controllerAs style (doing away with binding directly to $scope).

There have been many style opinions around this, of which I’ve adopted myself, primarily the var vm = this; declaration at the top of Controllers. Of recent months, I’ve been doing away with using vm inside the actual JavaScript Controller, and steering towards plain JavaScript variables and functions, and binding those I need out as some kind of “exports”.

To get a feel of what I was previously working from, let’s start from the var vm = this; days.

var vm = this;

This has been a really popular way of binding our variables to the Controller (which gets bound to $scope). Taking a simple example (note the // exports comment where I “bind” to the vm variable:

function MainCtrl () {

  var vm = this;

  function doSomething() {

  }

  // exports
  vm.doSomething = doSomething;

}

angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

This pattern is great (and has other variations such as binding vm.doSomething = function () {} directly without declaring the function above and assigning), and has been really helpful in working with Angular. The reason vm was created was to help with referencing the correct context inside other functions, as this doesn’t follow the lexical scoping rules like other variables do, so we assign this to vm to make a “reference”.

When you start having to bind a lot of things, we repeat vm so many times and end up with vm.* references all over our code. We don’t really need to bind directly to the this value, JavaScript can work on its own (such as updating var foo = {}; locally in a callback rather than vm.foo) for instance. An example with lots of vm.* bindings:

function MainCtrl () {

  var vm = this;

  function doSomething1() {}
  function doSomething2() {}
  function doSomething3() {}
  function doSomething4() {}
  function doSomething5() {}
  function doSomething6() {}

  // exports
  vm.doSomething1 = doSomething1;
  vm.doSomething2 = doSomething2;
  vm.doSomething3 = doSomething3;
  vm.doSomething4 = doSomething4;
  vm.doSomething5 = doSomething5;
  vm.doSomething6 = doSomething6;
}

angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Using angular.extend

This isn’t a new idea, as we all know angular.extend, but I came across this article from Modus Create which gave me the idea to completely drop all vm references given my current Angular Controller strategy/pattern. Their examples actually use angular.extend($scope, {...});, whereas my examples all adopt the controllerAs syntax.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

Here’s a quick example dropping vm references and just binding to this:

function MainCtrl () {
  this.someVar = {
    name: 'Todd'
  };
  this.anotherVar = [];
  this.doSomething = function doSomething() {

  };
}

angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Using angular.extend, we keep things cleaner and more Object-driven, and we can pass out a simple exports Object instead of a list of items:

function MainCtrl () {
  angular.extend(this, {
    someVar: {
      name: 'Todd'
    },
    anotherVar: [],
    doSomething: function doSomething() {

    }
  });
}

angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

This way, we’re not repeating the this keyword (or $scope if you’re still on that).

It also makes it a little easier to use “private” methods, and see clearly between them:

function MainCtrl () {

  // private
  function someMethod() {

  }

  // public
  var someVar = { name: 'Todd' };
  var anotherVar = [];
  function doSomething() {
    someMethod();
  }

  // exports
  angular.extend(this, {
    someVar: someVar,
    anotherVar: anotherVar,
    doSomething: doSomething
  });
}

angular
  .module('app')
  .controller('MainCtrl', MainCtrl);

Curious to hear thoughts and/or other practices.

Learn Angular the right way.

The most complete guide to learning Angular ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover