The goal of this style guide is to present a set of best practices and style guidelines for one AngularJS application.
Note: this is still a draft of the style guide, its main goal is to be community-driven so filling the gaps will be greatly appreciated by the whole community.
For AngularJS development recommended is the Google's JavaScript style guide.
In AngularJS's GitHub wiki there is a similar section by ProLoser, you can check it here.
Since a large AngularJS application has many components it's best to structure them in a directory hierarchy. There are two main approaches:
In this way the directory structure will look like:
TODO
Here is its layout:
TODO
TODO
This approach can be combined with both directory structures above.* One more slight variation of both directory structures is the one used in ng-boilerplate. In it, the unit tests for a given component are put in the folder where the component is located. This way when you make changes to a given component finding its test is easy. The tests also act as documentation and show use cases.
TODO
The app.js
file contains route definitions, configuration and/or manual bootstrap (if required).
Each JavaScript file should only hold a single component. The file should be named with the component's name.
Use Angular project structure template like Yeoman, ng-boilerplate.
I prefer the first structure because it makes common components easier to find.
Conventions about component naming can be found in each component section.
The HTML markup is important too and should be written by the team as if it were the same person.
TLDR; Put the scripts at the bottom.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
</head>
<body>
<div ng-app="myApp">
<div ng-view></div>
</div>
<script src="angular.js"></script>
<script src="app.js"></script>
</body>
</html>
Keep things simple and put AngularJS specific directives later. This way is easy to look to the code and find enhanced HTML by the framework (what improve the maintainibility).
<form class="frm" ng-submit="login.authenticate()">
<div>
<input class="ipt" type="text" placeholder="name" require ng-model="user.name">
</div>
</form>
Other HTML atributes should follow the Code Guide's recommendation
$digest
loop in each received message).bindonce
.$watch
as simple as possible. Making heavy and slow computations in a single $watch
will slow down the whole application (the $digest
loop is done in a single thread because of the single-threaded nature of JavaScript).$timeout
function to false to skip the $digest
loop when no watched variables are impacted by the invocation of the $timeout
callback function.$timeout
instead of setTimeout
$interval
instead of setInterval
$window
instead of window
$document
instead of document
$http
instead of $.ajax
This will make your testing easier and in some cases prevent unexpected behaviour (for example, if you missed $scope.$apply
in setTimeout
).
$q
) instead of callbacks. It will make your code look more elegant and clean, and save you from callback hell.$resource
instead of $http
when possible. The higher level of abstraction will save you from redundancy.$scope
. Only add functions and variables that are being used in the templates.ngInit
. The only appropriate use of ngInit
is for aliasing special properties of ngRepeat
. Besides this case, you should use controllers rather than ngInit
to initialize values on a scope.$
prefix for the names of variables, properties and methods. This prefix is reserved for AngularJS usage.b
is submodule of module a
you can nest them by using namespacing like: a.b
.There are two common ways for structuring the modules:
Currently there's not a big difference, but the first way looks cleaner. Also, if lazy-loading modules is implemented (currently not in the AngularJS roadmap), it will improve the app's performance.
Ctrl
in the end. The controllers are named UpperCamelCase (HomePageCtrl
, ShoppingCartCtrl
, AdminPanelCtrl
, etc.).module.controller('MyCtrl'
, function (dependency1, dependency2, ..., dependencyn) {
//...body
});
Using this type of definition avoids problems with minification. You can automatically generate the array definition from the standard one using tools like ng-annotate (and grunt task grunt-ng-annotate).* Use the original names of the controller's dependencies. This will help you produce more readable code:
module.controller('MyCtrl', ['$scope', function (s) {
//...body
}]);
which is less readable than:
module.controller('MyCtrl', ['$scope', function ($scope) {
//...body
}]);
This especially applies to a file that has so much code that you'd need to scroll through. This would possibly cause you to forget which variable is tied to which dependency.
$emit
, $broadcast
and $on
methods. The emitted and broadcasted messages should be kept to a minimum.$emit
, $broadcast
and manage it carefully because of name collisions and possible bugs.module.filter('myFormat', function () {
return function () {
//body...
};
});
module.controller('MyCtrl', ['$scope', 'myFormatFilter', function ($scope, myFormatFilter) {
//body...
}]);
scope
instead of $scope
in your link function. In the compile, post/pre link functions you have already defined arguments which will be passed when the function is invoked, you won't be able to change them using DI. This style is also used in AngularJS's source code.ng
or ui
prefixes since they are reserved for AngularJS and AngularJS UI usage.$scope.$on('$destroy', fn)
for cleaning up. This is especially useful when you're wrapping third-party plugins as directives.$sce
when you should deal with untrusted content.$digest
loop so creating a slow filter will slow down your app.service
instead of a factory
. In this way we can take advantage of the "klassical" inheritance easier:function Human() {
//body
}
Human.prototype.talk = function () {
return "I'm talking";
};
function Developer() {
//body
}
Developer.prototype = Object.create(Human.prototype);
Developer.prototype.code = function () {
return "I'm coding";
};
myModule.service('Human', Human);
myModule.service('Developer', Developer);
For session-level cache you can use $cacheFactory
. This should be used to cache results from requests or heavy computations.
ng-bind
or ng-cloak
instead of simple {{ }}
to prevent flashing content.src
of an image dynamically use ng-src
instead of src
with {{ }}
template.href
of an anchor tag dynamically use ng-href
instead of href
with {{ }}
template.style
attribute with {{ }}
, use the directive ng-style
with object-like parameters and scope variables as values:<script>
...
$scope.divStyle = {
width: 200,
position: 'relative'
};
...
</script>
<div ng-style="divStyle">my beautifully styled div which will work in IE</div>
Use resolve
to resolve dependencies before the view is shown.
TBD
Until this section is completed you can use this one.
Since the goal of this style guide is to be community-driven, contributions are greatly appriciated. For example, you can contribute by extending the Testing section or by translating the style guide to another language.