AngularJS Style Guides
Ryan Tucker
@rctucker88
Why Style Guides Matter
- Consistency
- Clear Communication
- Teamwork
Single Responsibility
// File1.js
angular
.module('app', ['ngRoute'])
.controller('SomeController', SomeController)
.factory('someFactory', someFactory);
function SomeController() { }
function someFactory() { }
All in one file
// File1.js
angular
.module('app')
.controller('SomeController', SomeController);
function SomeController() { }
// File2.js
angular
.module('app')
.factory('someFactory', someFactory);
function someFactory() { }
Versus
Split into multiple files
// module.js
angular
.module('app', ['ngRoute']);
IIFE
// logger.js
angular
.module('app')
.factory('logger', logger);
// logger function is added as a global variable
function logger() { }
// storage.js
angular
.module('app')
.factory('storage', storage);
// storage function is added as a global variable
function storage() { }
Avoid This
// logger.js
(function() {
'use strict';
angular
.module('app')
.factory('logger', logger);
function logger() { }
})();
// storage.js
(function() {
'use strict';
angular
.module('app')
.factory('storage', storage);
function storage() { }
})();
This is better, no globals left behind!
Naming Collisions
Use a unique naming convention for your module names! Separators are commonly used like app.users and app.login.
angular.module('myApp', ['app.users', 'app.login', 'app.items']);
Module Getters/Setters
Don't use a variable for a module. Since you only have one component per file anyway this shouldn't be an issue!
/* Avoid this! */
var app = angular.module('app', [
'ngAnimate',
'ngRoute',
'app.shared',
'app.dashboard'
]);
Use a chaining getter syntax instead.
angular
.module('app')
.controller('SomeController', SomeController);
function SomeController() { }
Named vs Anonymous Functions
Use named functioned over anonymous functions.
// dashboard.js
angular
.module('app')
.controller('Dashboard', Dashboard);
function Dashboard() { }
// logger.js
angular
.module('app')
.factory('logger', logger);
function logger() { }
Named Functions
Why? More readable code, easier to debug, and reduces nested callbacks.
controllerAs Syntax
Benefits in both the view and the controller.
View Benefits
- Makes your controllers closer to a JavaScript constructor
- Promotes using binding to a "dotted" object in the view
- Helps avoid $parent calls
<!-- avoid -->
<div ng-controller="Customer">
{{ name }}
</div>
<!-- recommended -->
<div ng-controller="Customer as customer">
{{ customer.name }}
</div>
Even Better
Capture the 'this' variable with a consistent variable name such as vm.
/* recommended */
function Customer() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
}
/* avoid */
function Customer() {
this.name = {};
this.sendMessage = function() { };
}
Controller Benefits
- Uses 'this' inside of controllers instead of the class $scope syntax
- Avoids temptation to use $scope methods which aren't always the best.
/* avoid */
function Customer($scope) {
$scope.name = {};
$scope.sendMessage = function() { };
}
/* Recommended */
function Customer() {
this.name = {};
this.sendMessage = function() { };
}
Formatting Your Controller
/* recommended */
function Sessions() {
var vm = this;
vm.gotoSession = gotoSession;
vm.refresh = refresh;
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions';
////////////
function gotoSession() {
/* */
}
function refresh() {
/* */
}
function search() {
/* */
}
Why is this helpful?
- Quickly see what your controller does by looking at the top
- More readable code
- Using function declarations avoids worrying about hoisting problems
Defer Controller Logic
Delegate your logic to a service where possible!
- Allows for logic reuse
- Logic can be more easily isolated for a unit test in a service
- Removes dependencies and implementation details from the controller
Keep Controllers Focused
Design a controller for a view, and try not to reuse it for other views.
Assigning a Controller
Assign the controller in your router and keep the logic out of the template!
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm'
});
}
<!-- avengers.html -->
<div>
</div>
Factories
- Use factories only
- Single responsibility
- Accessible members at the top
- Don't use anonymous functions for the factory's members
Directives
- One directive per file
- Only manipulate the DOM in a directive
- Provide a unique directive prefix
- Restrict to element and attributes
Minification and Annotation
- Always use minification syntax
- Use ng-annotate to automate minification
- Use Gulp or Grunt to further automate minification
Credits
Big shout-out to John Papa <@John_Papa> who organizes a large Angular style guide which this presentation is based on.
Find the style guide at https://github.com/johnpapa/angularjs-styleguide.
Questions?
Feel free to ask me questions on Twitter: @RCTucker88
AngularJS Style Guides
By Ryan Tucker
AngularJS Style Guides
- 1,830