Angular Icon Get 67% off the Angular Master Bundle!

See the bundle then add to cart and your discount is applied.

0 days
00 hours
00 mins
00 secs
Angularjs

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.

JavaScript Array Methods eBook Cover

🎉 Download it free!

Ready to go beyond ForEach? Get confident with advanced methods - Reduce, Find, Filter, Every, Some and Map.

  • Green Tick Icon Fully understand how to manage JavaScript Data Structures with immutable operations
  • Green Tick Icon 31 pages of deep-dive syntax, real-world examples, tips and tricks
  • Green Tick Icon Write cleaner and better-structured programming logic within 3 hours

As an extra bonus, we'll also send you some extra goodies across a few extra emails.

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.

Related blogs 🚀

Free eBooks:

JavaScript Array Methods eBook Cover

Ready to go beyond ForEach? Get confident with advanced methods - Reduce, Find, Filter, Every, Some and Map.

NestJS Build a RESTful CRUD API eBook Cover

Build your first NestJS app. With the CLI you'll learn the basics of real-world NestJS development.