Defining a single component per file helps with code maintenance. Rather than defining a module (and its dependencies), a controller, and a factory all in the same file, separate each one into their own files.
Wrapping your AngularJS components in an Immediately Invoked Function Expression (IIFE). This helps to prevent variables and function declarations from living longer than expected in the global scope, which also helps avoid variable collisions.
This becomes even more important when your code is minified and bundled into a single file for deployment to a production server by providing variable scope for each file.
When you include only a single component per file, there is rarely a need to introduce a variable for the module. Instead, use the simple getter syntax.
When using a module, using chaining with the getter syntax produces more readable code and avoids variables collisions or leaks.
A module should only be created once. Use
angular.module('app', ); to set a module and
angular.module('app'); to get a module.
Using named functions for callbacks produces more readable code, is much easier to debug, and reduces the amount of nested callback code.
controllerAssyntax in the view
controllerAs syntax over the
customer.name instead of
name), which is more contextual, easier to read, and avoids any reference issues that may occur without "dotting". It can also help avoid using
$parent calls in Views with nested controllers.
controllerAssyntax in the controller
controllerAs syntax instead of the
classic controller with $scope syntax helps avoid the temptation of using
$scope methods inside a controller when it might be better to avoid them or move them to a factory. Consider using
$scope in a factory, or if in a controller just when needed. For example when publishing and subscribing events using
$on consider moving these uses to a factory and invoke form the controller.
Inside the controller, the
controllerAs syntax uses
this, which gets bound to
$scope and is syntactic sugar over
$scope. You can still bind to the View and still access
this keyword is contextual and, when used within a function inside a controller, may change its context. Capturing the context of
this avoids the problem. Choose a consistent variable name such as
vm, which stands for ViewModel.
/* jshint validthis: true */comment avoids any jshint warnings.
Placing bindable memnbers at the top of the controller in alphabetical order makes it easy to ready and helps you instantly identify which members of the controller can be bound and used in the View.
When inline anonymous functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read.
Placing logic in a service or a factory, rather than directly in the controller, can lead to better resuse across multiple controllers. It is also easier to isolate in a unit test and allows the calling logic in the controller to be easily mocked. It also helps remove dependencies and hides implementation details from the controller.
Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using
ng-controller, that view is always associated with the same controller.
ng-controller="Avengers as vm"syntax.
All AngularJS services are singletons, which means that there is only one instance of a given service per injector. Services are instantiated with the
new keyword. Use
this for public methods and variables. You can also use a factory instead of a service, which can introduce greater consistency.
Factories are singletons which should have a single responsibility that is encapsulated by its context. Declaring all of the callable members of the service at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked).
Creating one directive per file, with the name the file matching the directive, makes them easier to maintain.
DOM manipulation can be difficult to test and debug. If you must manipulate the DOM directly, use a directive. However, if alternative ways can be used, such as using CSS to set styles or the animation services, Angular templating,
ngHide, use those instead.
If the directive makes sense as a standalone element, allow restrict
E (custom element) and optionally restrict
A (custom attribute). Generally,
E is appropriate if it could be its own control. In general, allow
EA but prefer
E when its standalone and
A when it enhances its existing DOM element.
Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller. Typically this should be done in an
When a controller depends on a promise to be resolved, resolve those dependencies in the
$routeProvider before the controller logic is executed. Using a route resolve allows the promise to resolve before the controller logic executes.
To prevent parameters from being converted to mangled variables, avoid using the shortcut syntax of declaring dependencies without using a minification-safe approach.
$inject to manually identify your dependencies mirros the technique used by
ng-annotate and safeguards your dependencies from being vulernable to minification issues when parameters may be mangled.
ng-annotate for Gulp or Grunt by commenting functions that need automated dependency injection with
/** @ngInject */ will safeguard your code from any dependencies that may not be using minification-safe practices.
ngStrictDiparameter on the
to create the injector in "strict-di" mode. This causes the application to fail to invoke functions which do not use explicit function annotation (these may not be minification safe). Debugging info will be logged to the console to help track down the offending code.
Providing a consistent structure that scales well, is modular, and makes it easy to find code quickly increases developer efficiency and productivity. You can achieve this by following the LIFT principle:
Locate your code quickly
Identify the code at a glance
Flattest structure you can
Try to follow the
Don't Repeat Yourself(DRY) pattern
jsDoc syntax to document function names, description, params and returns allows you to generate documentation from your code rather than writing it from scratch and provides consistency by using a common tool.
.jshintrc file is shown below.
Creating an AngularJS Constant for vendor libraries' global variables provides a way to inject vendor libraries that otherwise are globals. This improves code testability by allowing you to more easily know what the dependencies of your components are (avoids leaky abstractions).