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.