π Community-driven set of best practices for AngularJS application development
See the codeThe goal of this style guide is to present a set of best practices and style guidelines for one AngularJS application. These best practices are collected from:
Note 1: 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.
Note 2: before following any of the guidelines in the translations of the English document, make sure they are up-to date. The latest version of the AngularJS style guide is in the current document.
In this style guide you won't find common guidelines for JavaScript development. Such can be found at:
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 it in a directory hierarchy. There are two main approaches:
In this way the directory structure will look like:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ controllers
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.spec.js
βΒ Β βΒ Β βββ about
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ about
βΒ Β βΒ Β βββ directive2.js
βΒ Β βΒ Β βββ directive2.spec.js
βΒ Β βΒ Β βββ directive3.js
βΒ Β βΒ Β βββ directive3.spec.js
βΒ Β βββ filters
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βββ about
βΒ Β βββ services
βΒ Β βββ CommonService.js
βΒ Β βββ CommonService.spec.js
βΒ Β βββ cache
βΒ Β βΒ Β βββ Cache1.js
βΒ Β βΒ Β βββ Cache1.spec.js
βΒ Β βΒ Β βββ Cache2.js
βΒ Β βΒ Β βββ Cache2.spec.js
βΒ Β βββ models
βΒ Β βββ Model1.spec.js
βΒ Β βββ Model1.js
βΒ Β βββ Model2.spec.js
βΒ Β βββ Model2.js
βββ partials
βββ lib
βββ e2e-tests
Here is its layout:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ common
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βββ services
βΒ Β βββ home
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.spec.js
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βΒ Β βββ filter1.js
βΒ Β βΒ Β βΒ Β βββ filter1.spec.js
βΒ Β βΒ Β βΒ Β βββ filter2.js
βΒ Β βΒ Β βΒ Β βββ filter2.spec.js
βΒ Β βΒ Β βββ services
βΒ Β βΒ Β βββ service1.js
βΒ Β βΒ Β βββ service1.spec.js
βΒ Β βΒ Β βββ service2.js
βΒ Β βΒ Β βββ service2.spec.js
βΒ Β βββ about
βΒ Β βββ controllers
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β βΒ Β βββ directive2.js
βΒ Β βΒ Β βββ directive2.spec.js
βΒ Β βΒ Β βββ directive3.js
βΒ Β βΒ Β βββ directive3.spec.js
βΒ Β βββ filters
βΒ Β βΒ Β βββ filter3.js
βΒ Β βΒ Β βββ filter3.spec.js
βΒ Β βββ services
βΒ Β βββ service3.js
βΒ Β βββ service3.spec.js
βββ partials
βββ lib
βββ e2e-tests
app
βββ app.js
βββ my-complex-module
Β Β βββ controllers
Β Β βββ directives
Β Β βββ filters
Β Β βββ services
app
βββ directives
βββ directive1
βΒ Β βββ directive1.html
βΒ Β βββ directive1.js
βΒ Β βββ directive1.spec.js
βΒ Β βββ directive1.sass
βββ directive2
βββ directive2.html
βββ directive2.js
βββ directive2.spec.js
βββ directive2.sass
This approach can be combined with both directory structures above.
*.spec.js) should be located in the directory where the component is. 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.services
βββ cache
βΒ Β βββ cache1.js
βΒ Β βββ cache1.spec.js
βββ models
βββ model1.js
βββ model1.spec.js
app.js file should contain route definitions, configuration and/or manual bootstrap (if required).Conventions about component naming can be found in each component section.
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 after standard attributes. This will make it easier to skim your code and will make it easier to maintain because your attributes are consistently grouped and positioned.
<form class="frm" ng-submit="login.authenticate()">
<div>
<input class="ipt" type="text" placeholder="name" require ng-model="user.name">
</div>
</form>
Other HTML attributes should follow the Code Guide's recommendation
The following table is shown the naming conventions for every element:
| Element | Naming style | Example | usage |
|---|---|---|---|
| Modules | lowerCamelCase | angularApp | |
| Controllers | Functionality + 'Ctrl' | AdminCtrl | |
| Directives | lowerCamelCase | userInfo | |
| Filters | lowerCamelCase | userFilter | |
| Services | UpperCamelCase | User | constructor |
| Factories | lowerCamelCase | dataFactory | others |
$timeout instead of setTimeout$interval instead of setInterval$window instead of window$document instead of document$http instead of $.ajax$location instead of window.location or $window.location$cookies instead of document.cookieThis will make your testing easier and in some cases prevent unexpected behaviour (for example, if you missed $scope.$apply in setTimeout).
Automate your workflow using tools like:
Use promises ($q) instead of callbacks. It will make your code look more elegant and clean, and save you from callback hell.
Use $resource instead of $http when possible. The higher level of abstraction will save you from redundancy.
Use an AngularJS pre-minifier (ng-annotate) for preventing problems after minification.
Don't use globals. Resolve all dependencies using Dependency Injection, this will prevent bugs and monkey patching when testing.
Avoid globals by using Grunt/Gulp to wrap your code in Immediately Invoked Function Expression (IIFE). You can use plugins like grunt-wrap or gulp-wrap for this purpose. Example (using Gulp)
gulp.src("./src/*.js")
.pipe(wrap('(function(){\n"use strict";\n<%= contents %>\n})();'))
.pipe(gulp.dest("./dist"));
Do not pollute your $scope. Only add functions and variables that are being used in the templates.
Prefer the usage of controllers instead of ngInit. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope. The expression passed to ngInit should go through lexing, parsing and evaluation by the Angular interpreter implemented inside the $parse service. This leads to:
$parse service doesn't make a lot of sense in most cases, since ngInit expressions are often evaluated only onceDo not use $ prefix for the names of variables, properties and methods. This prefix is reserved for AngularJS usage.
Do not use JQUERY inside your app, If you must, use JQLite instead with angular.element.
When resolving dependencies through the DI mechanism of AngularJS, sort the dependencies by their type - the built-in AngularJS dependencies should be first, followed by your custom ones:
module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
return {
//Something
};
});
Modules should be named with lowerCamelCase. For indicating that module 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.
Do not manipulate DOM in your controllers, this will make your controllers harder for testing and will violate the Separation of Concerns principle. Use directives instead.
The naming of the controller is done using the controller's functionality (for example shopping cart, homepage, admin panel) and the substring Ctrl in the end.
Controllers are plain javascript constructors, so they will be named UpperCamelCase (HomePageCtrl, ShoppingCartCtrl, AdminPanelCtrl, etc.).
The controllers should not be defined as globals (even though AngularJS allows this, it is a bad practice to pollute the global namespace).
Use the following syntax for defining controllers:
function MyCtrl(dependency1, dependency2, ..., dependencyn) {
// ...
}
module.controller('MyCtrl', MyCtrl);
In order to prevent problems with minification, you can automatically generate the array definition syntax from the standard one using tools like ng-annotate (and grunt task grunt-ng-annotate).
Another alternative will be to use $inject like:
angular
.module('app')
.controller('HomepageCtrl', HomepageCtrl);
HomepageCtrl.$inject = ['$log', '$http', 'ngRoute'];
function HomepageCtrl($log, $http, ngRoute) {
// ...
}
Avoid use of $scope service to define functions and properties as part of controllers. Use $scope only if It's really needed:
0. For publish and subscribe to events: $scope.$emit, $scope.$broadcast, and $scope.$on.
0. For watch values or collections: $scope.$watch, $scope.$watchCollection
Prefer using controller as syntax and capture this using a variable:
<div ng-controller="MainCtrl as main">
{{ main.things }}
</div>
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$http'];
function MainCtrl ($http) {
var vm = this;
//a clearer visual connection on how is defined on the view
vm.title = 'Some title';
vm.description = 'Some description';
$http.get('/api/main/things').then(function (response) {
vm.things = response.data.things; // Adding 'things' as a property of the controller
});
}
Avoid using this keyword repeatedly inside a controller:
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$http'];
// Avoid
function MainCtrl ($http) {
this.title = 'Some title';
this.description = 'Some description';
$http.get('/api/main/things').then(function (response) {
// Warning! 'this' is in a different context here.
// The property will not be added as part of the controller context
this.things = response.data.things;
});
}
Using a consistent and short variable name is preferred, for example vm.
The main benefits of using this syntax:
$scope prototype chain. This is good practice since $scope prototype inheritance has some major drawbacks (this is probably the reason it was removed on Angular 2):
$scope when no need for special operations (as mentioned above). This is a good preparation for AngularJS V2.Digging more into controller as: digging-into-angulars-controller-as-syntax
If using array definition syntax, use the original names of the controller's dependencies. This will help you produce more readable code:
function MyCtrl(l, h) {
// ...
}
module.controller('MyCtrl', ['$log', '$http', MyCtrl]);
which is less readable than:
function MyCtrl($log, $http) {
// ...
}
module.controller('MyCtrl', ['$log', '$http', MyCtrl]);
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.
Make the controllers as lean as possible. Abstract commonly used functions into a service.
Avoid writing business logic inside controllers. Delegate business logic to a model, using a service.
For example:
//This is a common behaviour (bad example) of using business logic inside a controller.
angular.module('Store', [])
.controller('OrderCtrl', function () {
var vm = this;
vm.items = [];
vm.addToOrder = function (item) {
vm.items.push(item);//-->Business logic inside controller
};
vm.removeFromOrder = function (item) {
vm.items.splice(vm.items.indexOf(item), 1);//-->Business logic inside controller
};
vm.totalPrice = function () {
return vm.items.reduce(function (memo, item) {
return memo + (item.qty * item.price);//-->Business logic inside controller
}, 0);
};
});
When delegating business logic into a 'model' service, controller will look like this (see 'use services as your Model' for service-model implementation):
// order is used as a 'model'
angular.module('Store', [])
.controller('OrderCtrl', function (order) {
var vm = this;
vm.items = order.items;
vm.addToOrder = function (item) {
order.addToOrder(item);
};
vm.removeFromOrder = function (item) {
order.removeFromOrder(item);
};
vm.totalPrice = function () {
return order.total();
};
});
Why business logic / app state inside controllers is bad?
Communicate within different controllers using method invocation (possible when a child wants to communicate with its parent) or $emit, $broadcast and $on methods. The emitted and broadcasted messages should be kept to a minimum.
Make a list of all messages which are passed using $emit, $broadcast and manage it carefully because of name collisions and possible bugs.
Example:
// app.js
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Custom events:
- 'authorization-message' - description of the message
- { user, role, action } - data format
- user - a string, which contains the username
- role - an ID of the role the user has
- action - specific action the user tries to perform
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
When you need to format data encapsulate the formatting logic into a filter and declare it as dependency:
function myFormat() {
return function () {
// ...
};
}
module.filter('myFormat', myFormat);
function MyCtrl($scope, myFormatFilter) {
// ...
}
module.controller('MyCtrl', MyCtrl);
In case of nested controllers use "nested scoping" (the controllerAs syntax):
app.js
module.config(function ($routeProvider) {
$routeProvider
.when('/route', {
templateUrl: 'partials/template.html',
controller: 'HomeCtrl',
controllerAs: 'home'
});
});
HomeCtrl
function HomeCtrl() {
var vm = this;
vm.bindingValue = 42;
}
template.html
<div ng-bind="home.bindingValue"></div>
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.This section includes information about the service component in AngularJS. It is not dependent of the way of definition (i.e. as provider, .factory, .service), except if explicitly mentioned.
Use camelCase to name your services.
UpperCamelCase (PascalCase) for naming your services, used as constructor functions i.e.:
function MainCtrl(User) {
var vm = this;
vm.user = new User('foo', 42);
}
module.controller('MainCtrl', MainCtrl);
function User(name, age) {
this.name = name;
this.age = age;
}
module.factory('User', function () {
return User;
});
lowerCamelCase for all other services.
Encapsulate all the business logic in services. Prefer using it as your model. For example:
// order is the 'model'
angular.module('Store')
.factory('order', function () {
var add = function (item) {
this.items.push (item);
};
var remove = function (item) {
if (this.items.indexOf(item) > -1) {
this.items.splice(this.items.indexOf(item), 1);
}
};
var total = function () {
return this.items.reduce(function (memo, item) {
return memo + (item.qty * item.price);
}, 0);
};
return {
items: [],
addToOrder: add,
removeFromOrder: remove,
totalPrice: total
};
});
See 'Avoid writing business logic inside controllers' for an example of a controller consuming this service.
Services representing the domain preferably a 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.
If given service requires configuration define the service as provider and configure it in the config callback like:
angular.module('demo', [])
.config(function ($provide) {
$provide.provider('sample', function () {
var foo = 42;
return {
setFoo: function (f) {
foo = f;
},
$get: function () {
return {
foo: foo
};
}
};
});
});
var demo = angular.module('demo');
demo.config(function (sampleProvider) {
sampleProvider.setFoo(41);
});
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: <div ng-controller="MainCtrl as main">
<div ng-style="main.divStyle">my beautifully styled div which will work in IE</div>;
</div>
angular
.module('app')
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = [];
function MainCtrl() {
var vm = this;
vm.divStyle = {
width: 200,
position: 'relative'
};
}
resolve to resolve dependencies before the view is shown.resolve callback. Isolate all the requests inside appropriate services. This way you can enable caching and follow the separation of concerns principle.E2E tests are the next common sense step after unit tests, that will allow you to trace bugs and errors in the behaviour of your system. They are great for providing a sanity check that most common scenarios of using your application works. This way you can automate the process and run it each time before you deploy your application.
Ideally, Angular End-to-End tests are written in Jasmine. These tests are run using the Protractor E2E test runner which uses native events and has special features for Angular applications.
File structure:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ home
βΒ Β βΒ Β βββ home.html
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βΒ Β βββ filter1.js
βΒ Β βΒ Β βΒ Β βββ filter1.spec.js
βΒ Β βΒ Β βββ services
βΒ Β βΒ Β βββ service1.js
βΒ Β βΒ Β βββ service1.spec.js
βΒ Β βββ about
βΒ Β βββ about.html
βΒ Β βββ controllers
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β Β Β βββ directive2.js
βΒ Β Β Β βββ directive2.spec.js
βββ partials
βββ lib
βββ e2e-tests
βββ protractor.conf.js
βββ specs
βββ home.js
βββ about.js
angular-translate.Optimize the digest cycle
$digest loop explicitly (it should happen only in exceptional cases), invoke it only when required (for example: when using real-time communication, don't cause a $digest loop in each received message).bindonce for older versions of AngularJS or one-time bindings in AngularJS >=1.3.0.
<div>
{{ ::main.things }}
</div>
or
<div ng-bind="::main.things"></div>
After that, no watchers will be created for main.things and any changes of main.things will not update the view.$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).$watchCollection, which performs a shallow check for equality of the result of the watched expression and the previous value of the expression's evaluation.$timeout function to false to skip the $digest loop when no watched variables are impacted by the invocation of the $timeout callback function.Consider decreasing number of network requests by bundling/caching html template files into your main javascript file, using grunt-html2js / gulp-html2js. See here and here for details. This is particularly useful when the project has a lot of small html templates that can be a part of the main (minified and gzipped) javascript file.
Since the goal of this style guide is to be community-driven, contributions are greatly appreciated. For example, you can contribute by extending the Testing section or by translating the style guide to your language.
1,043 followers Β· starred Oct 2013
449 followers Β· starred Oct 2013
631 followers Β· starred Oct 2013
25 followers Β· starred Feb 2014
π Community-driven set of best practices for AngularJS application development
See the codeThe goal of this style guide is to present a set of best practices and style guidelines for one AngularJS application. These best practices are collected from:
Note 1: 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.
Note 2: before following any of the guidelines in the translations of the English document, make sure they are up-to date. The latest version of the AngularJS style guide is in the current document.
In this style guide you won't find common guidelines for JavaScript development. Such can be found at:
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 it in a directory hierarchy. There are two main approaches:
In this way the directory structure will look like:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ controllers
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.spec.js
βΒ Β βΒ Β βββ about
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ about
βΒ Β βΒ Β βββ directive2.js
βΒ Β βΒ Β βββ directive2.spec.js
βΒ Β βΒ Β βββ directive3.js
βΒ Β βΒ Β βββ directive3.spec.js
βΒ Β βββ filters
βΒ Β βΒ Β βββ home
βΒ Β βΒ Β βββ about
βΒ Β βββ services
βΒ Β βββ CommonService.js
βΒ Β βββ CommonService.spec.js
βΒ Β βββ cache
βΒ Β βΒ Β βββ Cache1.js
βΒ Β βΒ Β βββ Cache1.spec.js
βΒ Β βΒ Β βββ Cache2.js
βΒ Β βΒ Β βββ Cache2.spec.js
βΒ Β βββ models
βΒ Β βββ Model1.spec.js
βΒ Β βββ Model1.js
βΒ Β βββ Model2.spec.js
βΒ Β βββ Model2.js
βββ partials
βββ lib
βββ e2e-tests
Here is its layout:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ common
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βββ services
βΒ Β βββ home
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.js
βΒ Β βΒ Β βΒ Β βββ SecondCtrl.spec.js
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βΒ Β βββ filter1.js
βΒ Β βΒ Β βΒ Β βββ filter1.spec.js
βΒ Β βΒ Β βΒ Β βββ filter2.js
βΒ Β βΒ Β βΒ Β βββ filter2.spec.js
βΒ Β βΒ Β βββ services
βΒ Β βΒ Β βββ service1.js
βΒ Β βΒ Β βββ service1.spec.js
βΒ Β βΒ Β βββ service2.js
βΒ Β βΒ Β βββ service2.spec.js
βΒ Β βββ about
βΒ Β βββ controllers
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β βΒ Β βββ directive2.js
βΒ Β βΒ Β βββ directive2.spec.js
βΒ Β βΒ Β βββ directive3.js
βΒ Β βΒ Β βββ directive3.spec.js
βΒ Β βββ filters
βΒ Β βΒ Β βββ filter3.js
βΒ Β βΒ Β βββ filter3.spec.js
βΒ Β βββ services
βΒ Β βββ service3.js
βΒ Β βββ service3.spec.js
βββ partials
βββ lib
βββ e2e-tests
app
βββ app.js
βββ my-complex-module
Β Β βββ controllers
Β Β βββ directives
Β Β βββ filters
Β Β βββ services
app
βββ directives
βββ directive1
βΒ Β βββ directive1.html
βΒ Β βββ directive1.js
βΒ Β βββ directive1.spec.js
βΒ Β βββ directive1.sass
βββ directive2
βββ directive2.html
βββ directive2.js
βββ directive2.spec.js
βββ directive2.sass
This approach can be combined with both directory structures above.
*.spec.js) should be located in the directory where the component is. 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.services
βββ cache
βΒ Β βββ cache1.js
βΒ Β βββ cache1.spec.js
βββ models
βββ model1.js
βββ model1.spec.js
app.js file should contain route definitions, configuration and/or manual bootstrap (if required).Conventions about component naming can be found in each component section.
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 after standard attributes. This will make it easier to skim your code and will make it easier to maintain because your attributes are consistently grouped and positioned.
<form class="frm" ng-submit="login.authenticate()">
<div>
<input class="ipt" type="text" placeholder="name" require ng-model="user.name">
</div>
</form>
Other HTML attributes should follow the Code Guide's recommendation
The following table is shown the naming conventions for every element:
| Element | Naming style | Example | usage |
|---|---|---|---|
| Modules | lowerCamelCase | angularApp | |
| Controllers | Functionality + 'Ctrl' | AdminCtrl | |
| Directives | lowerCamelCase | userInfo | |
| Filters | lowerCamelCase | userFilter | |
| Services | UpperCamelCase | User | constructor |
| Factories | lowerCamelCase | dataFactory | others |
$timeout instead of setTimeout$interval instead of setInterval$window instead of window$document instead of document$http instead of $.ajax$location instead of window.location or $window.location$cookies instead of document.cookieThis will make your testing easier and in some cases prevent unexpected behaviour (for example, if you missed $scope.$apply in setTimeout).
Automate your workflow using tools like:
Use promises ($q) instead of callbacks. It will make your code look more elegant and clean, and save you from callback hell.
Use $resource instead of $http when possible. The higher level of abstraction will save you from redundancy.
Use an AngularJS pre-minifier (ng-annotate) for preventing problems after minification.
Don't use globals. Resolve all dependencies using Dependency Injection, this will prevent bugs and monkey patching when testing.
Avoid globals by using Grunt/Gulp to wrap your code in Immediately Invoked Function Expression (IIFE). You can use plugins like grunt-wrap or gulp-wrap for this purpose. Example (using Gulp)
gulp.src("./src/*.js")
.pipe(wrap('(function(){\n"use strict";\n<%= contents %>\n})();'))
.pipe(gulp.dest("./dist"));
Do not pollute your $scope. Only add functions and variables that are being used in the templates.
Prefer the usage of controllers instead of ngInit. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope. The expression passed to ngInit should go through lexing, parsing and evaluation by the Angular interpreter implemented inside the $parse service. This leads to:
$parse service doesn't make a lot of sense in most cases, since ngInit expressions are often evaluated only onceDo not use $ prefix for the names of variables, properties and methods. This prefix is reserved for AngularJS usage.
Do not use JQUERY inside your app, If you must, use JQLite instead with angular.element.
When resolving dependencies through the DI mechanism of AngularJS, sort the dependencies by their type - the built-in AngularJS dependencies should be first, followed by your custom ones:
module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
return {
//Something
};
});
Modules should be named with lowerCamelCase. For indicating that module 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.
Do not manipulate DOM in your controllers, this will make your controllers harder for testing and will violate the Separation of Concerns principle. Use directives instead.
The naming of the controller is done using the controller's functionality (for example shopping cart, homepage, admin panel) and the substring Ctrl in the end.
Controllers are plain javascript constructors, so they will be named UpperCamelCase (HomePageCtrl, ShoppingCartCtrl, AdminPanelCtrl, etc.).
The controllers should not be defined as globals (even though AngularJS allows this, it is a bad practice to pollute the global namespace).
Use the following syntax for defining controllers:
function MyCtrl(dependency1, dependency2, ..., dependencyn) {
// ...
}
module.controller('MyCtrl', MyCtrl);
In order to prevent problems with minification, you can automatically generate the array definition syntax from the standard one using tools like ng-annotate (and grunt task grunt-ng-annotate).
Another alternative will be to use $inject like:
angular
.module('app')
.controller('HomepageCtrl', HomepageCtrl);
HomepageCtrl.$inject = ['$log', '$http', 'ngRoute'];
function HomepageCtrl($log, $http, ngRoute) {
// ...
}
Avoid use of $scope service to define functions and properties as part of controllers. Use $scope only if It's really needed:
0. For publish and subscribe to events: $scope.$emit, $scope.$broadcast, and $scope.$on.
0. For watch values or collections: $scope.$watch, $scope.$watchCollection
Prefer using controller as syntax and capture this using a variable:
<div ng-controller="MainCtrl as main">
{{ main.things }}
</div>
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$http'];
function MainCtrl ($http) {
var vm = this;
//a clearer visual connection on how is defined on the view
vm.title = 'Some title';
vm.description = 'Some description';
$http.get('/api/main/things').then(function (response) {
vm.things = response.data.things; // Adding 'things' as a property of the controller
});
}
Avoid using this keyword repeatedly inside a controller:
app.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$http'];
// Avoid
function MainCtrl ($http) {
this.title = 'Some title';
this.description = 'Some description';
$http.get('/api/main/things').then(function (response) {
// Warning! 'this' is in a different context here.
// The property will not be added as part of the controller context
this.things = response.data.things;
});
}
Using a consistent and short variable name is preferred, for example vm.
The main benefits of using this syntax:
$scope prototype chain. This is good practice since $scope prototype inheritance has some major drawbacks (this is probably the reason it was removed on Angular 2):
$scope when no need for special operations (as mentioned above). This is a good preparation for AngularJS V2.Digging more into controller as: digging-into-angulars-controller-as-syntax
If using array definition syntax, use the original names of the controller's dependencies. This will help you produce more readable code:
function MyCtrl(l, h) {
// ...
}
module.controller('MyCtrl', ['$log', '$http', MyCtrl]);
which is less readable than:
function MyCtrl($log, $http) {
// ...
}
module.controller('MyCtrl', ['$log', '$http', MyCtrl]);
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.
Make the controllers as lean as possible. Abstract commonly used functions into a service.
Avoid writing business logic inside controllers. Delegate business logic to a model, using a service.
For example:
//This is a common behaviour (bad example) of using business logic inside a controller.
angular.module('Store', [])
.controller('OrderCtrl', function () {
var vm = this;
vm.items = [];
vm.addToOrder = function (item) {
vm.items.push(item);//-->Business logic inside controller
};
vm.removeFromOrder = function (item) {
vm.items.splice(vm.items.indexOf(item), 1);//-->Business logic inside controller
};
vm.totalPrice = function () {
return vm.items.reduce(function (memo, item) {
return memo + (item.qty * item.price);//-->Business logic inside controller
}, 0);
};
});
When delegating business logic into a 'model' service, controller will look like this (see 'use services as your Model' for service-model implementation):
// order is used as a 'model'
angular.module('Store', [])
.controller('OrderCtrl', function (order) {
var vm = this;
vm.items = order.items;
vm.addToOrder = function (item) {
order.addToOrder(item);
};
vm.removeFromOrder = function (item) {
order.removeFromOrder(item);
};
vm.totalPrice = function () {
return order.total();
};
});
Why business logic / app state inside controllers is bad?
Communicate within different controllers using method invocation (possible when a child wants to communicate with its parent) or $emit, $broadcast and $on methods. The emitted and broadcasted messages should be kept to a minimum.
Make a list of all messages which are passed using $emit, $broadcast and manage it carefully because of name collisions and possible bugs.
Example:
// app.js
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Custom events:
- 'authorization-message' - description of the message
- { user, role, action } - data format
- user - a string, which contains the username
- role - an ID of the role the user has
- action - specific action the user tries to perform
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
When you need to format data encapsulate the formatting logic into a filter and declare it as dependency:
function myFormat() {
return function () {
// ...
};
}
module.filter('myFormat', myFormat);
function MyCtrl($scope, myFormatFilter) {
// ...
}
module.controller('MyCtrl', MyCtrl);
In case of nested controllers use "nested scoping" (the controllerAs syntax):
app.js
module.config(function ($routeProvider) {
$routeProvider
.when('/route', {
templateUrl: 'partials/template.html',
controller: 'HomeCtrl',
controllerAs: 'home'
});
});
HomeCtrl
function HomeCtrl() {
var vm = this;
vm.bindingValue = 42;
}
template.html
<div ng-bind="home.bindingValue"></div>
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.This section includes information about the service component in AngularJS. It is not dependent of the way of definition (i.e. as provider, .factory, .service), except if explicitly mentioned.
Use camelCase to name your services.
UpperCamelCase (PascalCase) for naming your services, used as constructor functions i.e.:
function MainCtrl(User) {
var vm = this;
vm.user = new User('foo', 42);
}
module.controller('MainCtrl', MainCtrl);
function User(name, age) {
this.name = name;
this.age = age;
}
module.factory('User', function () {
return User;
});
lowerCamelCase for all other services.
Encapsulate all the business logic in services. Prefer using it as your model. For example:
// order is the 'model'
angular.module('Store')
.factory('order', function () {
var add = function (item) {
this.items.push (item);
};
var remove = function (item) {
if (this.items.indexOf(item) > -1) {
this.items.splice(this.items.indexOf(item), 1);
}
};
var total = function () {
return this.items.reduce(function (memo, item) {
return memo + (item.qty * item.price);
}, 0);
};
return {
items: [],
addToOrder: add,
removeFromOrder: remove,
totalPrice: total
};
});
See 'Avoid writing business logic inside controllers' for an example of a controller consuming this service.
Services representing the domain preferably a 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.
If given service requires configuration define the service as provider and configure it in the config callback like:
angular.module('demo', [])
.config(function ($provide) {
$provide.provider('sample', function () {
var foo = 42;
return {
setFoo: function (f) {
foo = f;
},
$get: function () {
return {
foo: foo
};
}
};
});
});
var demo = angular.module('demo');
demo.config(function (sampleProvider) {
sampleProvider.setFoo(41);
});
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: <div ng-controller="MainCtrl as main">
<div ng-style="main.divStyle">my beautifully styled div which will work in IE</div>;
</div>
angular
.module('app')
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = [];
function MainCtrl() {
var vm = this;
vm.divStyle = {
width: 200,
position: 'relative'
};
}
resolve to resolve dependencies before the view is shown.resolve callback. Isolate all the requests inside appropriate services. This way you can enable caching and follow the separation of concerns principle.E2E tests are the next common sense step after unit tests, that will allow you to trace bugs and errors in the behaviour of your system. They are great for providing a sanity check that most common scenarios of using your application works. This way you can automate the process and run it each time before you deploy your application.
Ideally, Angular End-to-End tests are written in Jasmine. These tests are run using the Protractor E2E test runner which uses native events and has special features for Angular applications.
File structure:
.
βββ app
βΒ Β βββ app.js
βΒ Β βββ home
βΒ Β βΒ Β βββ home.html
βΒ Β βΒ Β βββ controllers
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.js
βΒ Β βΒ Β βΒ Β βββ FirstCtrl.spec.js
βΒ Β βΒ Β βββ directives
βΒ Β βΒ Β βΒ Β βββ directive1.js
βΒ Β βΒ Β βΒ Β βββ directive1.spec.js
βΒ Β βΒ Β βββ filters
βΒ Β βΒ Β βΒ Β βββ filter1.js
βΒ Β βΒ Β βΒ Β βββ filter1.spec.js
βΒ Β βΒ Β βββ services
βΒ Β βΒ Β βββ service1.js
βΒ Β βΒ Β βββ service1.spec.js
βΒ Β βββ about
βΒ Β βββ about.html
βΒ Β βββ controllers
βΒ Β βΒ Β βββ ThirdCtrl.js
βΒ Β βΒ Β βββ ThirdCtrl.spec.js
βΒ Β βββ directives
βΒ Β Β Β βββ directive2.js
βΒ Β Β Β βββ directive2.spec.js
βββ partials
βββ lib
βββ e2e-tests
βββ protractor.conf.js
βββ specs
βββ home.js
βββ about.js
angular-translate.Optimize the digest cycle
$digest loop explicitly (it should happen only in exceptional cases), invoke it only when required (for example: when using real-time communication, don't cause a $digest loop in each received message).bindonce for older versions of AngularJS or one-time bindings in AngularJS >=1.3.0.
<div>
{{ ::main.things }}
</div>
or
<div ng-bind="::main.things"></div>
After that, no watchers will be created for main.things and any changes of main.things will not update the view.$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).$watchCollection, which performs a shallow check for equality of the result of the watched expression and the previous value of the expression's evaluation.$timeout function to false to skip the $digest loop when no watched variables are impacted by the invocation of the $timeout callback function.Consider decreasing number of network requests by bundling/caching html template files into your main javascript file, using grunt-html2js / gulp-html2js. See here and here for details. This is particularly useful when the project has a lot of small html templates that can be a part of the main (minified and gzipped) javascript file.
Since the goal of this style guide is to be community-driven, contributions are greatly appreciated. For example, you can contribute by extending the Testing section or by translating the style guide to your language.
1,043 followers Β· starred Oct 2013
449 followers Β· starred Oct 2013
631 followers Β· starred Oct 2013
25 followers Β· starred Feb 2014