AngularJS StyleGuide

Controllers

  • controllerAs syntax: Controllers are classes, so use the controllerAs syntax at all times

    <!-- avoid -->
    <div ng-controller="MainCtrl">
      {{ someObject }}
    </div>
    
    <!-- recommended -->
    <div ng-controller="MainCtrl as vm">
      {{ vm.someObject }}
    </div>
    
  • In the DOM we get a variable per controller, which aids nested controller methods, avoiding any $parent calls

  • The controllerAs syntax uses this inside controllers, which gets bound to $scope

    // avoid
    function MainCtrl ($scope) {
      $scope.someObject = {};
      $scope.doSomething = function () {
    
      };
    }
    
    // recommended
    function MainCtrl () {
      this.someObject = {};
      this.doSomething = function () {
    
      };
    }
    
  • Only use $scope in controllerAs when necessary; for example, publishing and subscribing events using $emit, $broadcast, $on or $watch. Try to limit the use of these, however, and treat $scope as a special use case

  • Inheritance: Use prototypal inheritance when extending controller classes

    function BaseCtrl () {
      this.doSomething = function () {
    
      };
    }
    BaseCtrl.prototype.someObject = {};
    BaseCtrl.prototype.sharedSomething = function () {
    
    };
    
    AnotherCtrl.prototype = Object.create(BaseCtrl.prototype);
    
    function AnotherCtrl () {
      this.anotherSomething = function () {
    
      };
    }
    
  • Use Object.create with a polyfill for browser support

  • controllerAs 'vm': Capture the this context of the Controller using vm, standing for ViewModel

    // avoid
    function MainCtrl () {
      this.doSomething = function () {
    
      };
    }
    
    // recommended
    function MainCtrl (SomeService) {
      var vm = this;
      vm.doSomething = SomeService.doSomething;
    }
    

    Why? : Function context changes the this value, use it to avoid .bind() calls and scoping issues

  • Presentational logic only (MVVM): Presentational logic only inside a controller, avoid Business logic (delegate to Services)

    // avoid
    function MainCtrl () {
    
      var vm = this;
    
      $http
        .get('/users')
        .success(function (response) {
          vm.users = response;
        });
    
      vm.removeUser = function (user, index) {
        $http
          .delete('/user/' + user.id)
          .then(function (response) {
            vm.users.splice(index, 1);
          });
      };
    
    }
    
    // recommended
    function MainCtrl (UserService) {
    
      var vm = this;
    
      UserService
        .getUsers()
        .then(function (response) {
          vm.users = response;
        });
    
      vm.removeUser = function (user, index) {
        UserService
          .removeUser(user)
          .then(function (response) {
            vm.users.splice(index, 1);
          });
      };
    
    }
    

    Why? : Controllers should fetch Model data from Services, avoiding any Business logic. Controllers should act as a ViewModel and control the data flowing between the Model and the View presentational layer. Business logic in Controllers makes testing Services impossible.