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 $.ajaxThis 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.