Skip to content
This repository has been archived by the owner on Oct 30, 2019. It is now read-only.

Coding Guidelines for Angular JS

brian-murray35 edited this page Aug 19, 2015 · 6 revisions

Table of Contents

Miscellaneous Code Conventions

###Miscellaneous Code Conventions

  • Use semicolons ;
  • Commas last ,
  • 2 spaces for indentation (no tabs)
  • Prefer ' over "
  • 'use strict';
  • 80 character line length
  • Write "attractive" code

###Javascript

###Naming

  • Do not name your modules/variables with the $ prefix or $$, that is for core components.
  • Do not prefix your directives or controller with ng- or ui- or elsewhere.
  • Use CamelCase.
  • Append the controller name with the suffix Controller or with no suffix. Choose 1, not both.
  • Use short custom prefixes for your directives to prevent name collisions with third-party libraries.
  • Use consistent names for all components following a pattern that describes the component's feature then (optionally) its type. e.g. feature.type.js.
// Controllers
avengers.js
avengers.controller.js
avengersController.js 

// Services/Factories
logger.js
logger.service.js
loggerService.js 
  • Name test specifications similar to the component they test with a suffix of spec.
avengers.controller.spec.js
logger.service.spec.js
avengers.routes.spec.js
avenger-profile.directive.spec.js

  • When there are multiple modules, the main module file is named app.module.js while other dependent modules are named after what they represent. For example, an admin module is named admin.module.js. The respective registered module names would be app and admin. A single module app might be named app.js, omitting the module moniker.

Constants

  • Vendor Globals: Create an AngularJS Constant for vendor libraries' global variables.
// constants.js
/* global toastr:false, moment:false */
(function() {
'use strict';
angular
    .module('app.core')
    .constant('toastr', toastr)
    .constant('moment', moment);
})();

Markup

  • Put scripts at the bottom of your HTML

  • Put Angular attributes at the end of element declarations

### <a name="Project Layout"></a>Project Layout
  • Use folders for modules rather than splitting on class type (directive/controller/model)
|-- app.js
|-- dashboard/
|   |-- DashboardService.js
|   |-- DashboardCtrl.js
|-- login/
|   |-- LoginService.js
|   |-- LoginCtrl.js
|-- inbox/
|   |-- InboxService.js
|   |-- InboxCtrl.js

Modules

  • Use multiple modules. Do not put everything into the main controller or module.
  • Keep the App Module Thin: Only put logic for pulling together the app in the application module. Leave features in their own modules
  • Create modules that represent feature areas, such as layout, reusable and shared services, dashboards, and app specific features. (Avoid Naming Collisions: Use unique naming conventions with separators for sub-modules.)
  • Create modules that represent reusable application blocks for common services such as exception handling, logging, diagnostics, security, and local data stashing.
  • Add services and controllers to a module and then add the module to the app.
  • Use named functions instead of passing an anonymous function in as a callback.
// dashboard.js
angular
    .module('app')
    .controller('Dashboard', Dashboard);
  • Wrap AngularJS components in an Immediately Invoked Function Expression (IIFE).
  'use strict';
module.exports = function($scope) {
      $scope.foo = 'bar';
      …	
  };

*Only set a module once and get for all other instances.

  • Use angular.module('app', []); to set a module.
  • Use angular.module('app'); to get a module.

Global Dependencies

  • Encapsulate globals into a module so that they can be injected.

Bindings

  • For content that is only loaded once consider using bindonce.
  • Avoid too many watchers. Make computation in any watchers as simple as possible.
  • Set the third parameter in $timeout to false when no watched variables are impacted to avoid the $digest cycle.

Controllers

  • Never do DOM manipulation in a controller.
  • Business logic should live in services and not in controllers.
  • Use routing instead of binding a controller directly to the DOM.
angular
    .module('app')
    .config(config);
function config($routeProvider) {
    $routeProvider
        .when('/avengers', {
            templateUrl: 'avengers.html',
            controller: 'Avengers',
            controllerAs: 'vm'
        });
}
  • Controllers should not be defined as globals.
  • Place bindable members at the top of the controller.
  • Use function declarations to hide implementation details.
function Avengers(dataservice, logger) {
    var vm = this;
    vm.avengers = [];
    vm.title = 'Avengers';
    var activate = function() {
        return getAvengers().then(function() {
            logger.info('Activated Avengers View');
        });
    }
    var getAvengers = function() {
        return dataservice.getAvengers().then(function(data) {
            vm.avengers = data;
            return vm.avengers;
        });
    }
    vm.getAvengers = getAvengers;
    activate();
}
  • Defer logic in a controller by delegating to services and factories.
function Order(creditService) {
    var vm = this;
    vm.checkCredit = checkCredit;
    vm.total = 0;
    function checkCredit() { 
       return creditService.check();
    };
}
  • Use the controllerAs syntax over the classic controller with $scope syntax.
<div ng-controller="Customer as customer">
   {{ customer.name }}
</div>
</Code></pre>
* Use a capture variable for this when using the controllerAs syntax e.g.
<Code><pre>
function Customer() {
    var vm = this;
    vm.name = {};
    vm.sendMessage = function() { };
}

Directives

  • DOM manipulation should be done in the link method.
  • One directive per file.
  • Restrict to elements and attributes
  • Use 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.
  • Create an isolated scope when you develop reusable components.
  • Use directives as attributes or elements instead of comments or classes, this will make your code more readable.
  • Use $scope.$on('$destroy', fn) for cleaning up. This is especially useful when you're wrapping third-party plugins as directives.
  • Extend directives by using Directive Controllers: You can place methods and properties into a directive-controller, and access that same controller from other directives. You can even override methods and properties through this relationship
  • Do not forget to use $sce when you should deal with untrusted content.

Filters

  • Encapsulate formatting in Filters.
  • Make your filters as light as possible. They are called often during the $digest loop so creating a slow filter will slow down your app.
  • Do a single thing in your filters, keep them coherent. More complex manipulations can be achieved by piping existing filters.

Services/Factories

  • $timeout instead of setTimeout
  • $interval instead of setInterval
  • $window instead of window
  • $document instead of document
  • $http instead of $.ajax
  • Use promises ($q) instead of callbacks
  • $resource instead of $http whenever possible
  • 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 configcallback like:
  • Factories should have a single responsibility
  • Accessible members should be at the top
  • Use function declarations to hide implementation details.

Data Services

  • Separate Data Calls: Refactor logic for making data operations and interacting with data to a factory. Make data services responsible for XHR calls, local storage, stashing in memory, or any other data operations.
  • Return a Promise from Data Calls: When calling a data service that returns a promise such as $http, return a promise in your calling function too.

Templates

  • Use ng-bind or ng-cloak instead of simple {{ }} to prevent flashing content.
  • Avoid writing complex expressions in the templates.
  • When you need to set the src of an image dynamically use ng-src instead of src with {{ }}template.
  • When you need to set the href of an anchor tag dynamically use ng-href instead of href with{{ }} template.
  • Instead of using scope variable as string and using it with style attribute with {{ }}, use the directive ng-style with object-like parameters and scope variables as values:

Routing

  • Use resolve to resolve dependencies before the view is shown.

Use Batarang

  • Learn to use the Chrome extension to debug you apps. In particular note the Angular tab on the elements view when you inspect an element – it will show the bound scope and its contents!

Scopes

  • Be aware that scopes inherit from the parent scope. Watch out for shadowing of variables.
  • Directives on the other hand isolate scope and only inherit explicitly declared properties.
  • Avoid $scope.$watch where possible, or unless unavoidable.

Exception Handling

  • Exception Catchers: Create a factory that exposes an interface to catch and gracefully handle exceptions.
angular
    .module('blocks.exception')
    .factory('exception', exception);
exception.$inject = ['logger'];
function exception(logger) {
    var service = {
        catcher: catcher
    };
    return service;
    function catcher(message) {
        return function(reason) {
            logger.error(message, reason);
        };
    }
}
  • Route Errors: Handle and log all routing errors using $routeChangeError.
function handleRoutingErrors() {
    /**
     * Route cancellation:
     * On routing error, go to the dashboard.
     * Provide an exit clause if it tries to do it twice.
     */
    $rootScope.$on('$routeChangeError',
        function(event, current, previous, rejection) {
            var destination = (current && (current.title || current.name || current.loadedTemplateUrl)) ||
                'unknown target';
            var msg = 'Error routing to ' + destination + '. ' + (rejection.msg || '');
            /**
             * Optionally log using a custom service or $log.
             * (Don't forget to inject custom service)
             */
            logger.warning(msg, [current]);
        }
    );
}
  • decorators: Use a decorator, at config time using the $provide service, on the $exceptionHandler service to perform custom actions when exceptions occur.

Broadcast Messages

Avoid broadcasting messages/events on the $rootscope. Instead create a custom service for each event you want to broadcast.

.service("hiEventService",function($rootScope) {
    this.broadcast = function() {$rootScope.$broadcast("hi")}
    this.listen = function(callback) {$rootScope.$on("hi",callback)}
})

Testing

  • Angular has built-in support for testing, see ngMock, Jasmine, Protractor and Karma.

JQuery

  • Do not use JQuery. Just don’t do it !
  • Don't wrap element inside of $(). All AngularJS elements are already jq-objects
  • Don't do if (!$scope.$$phase) $scope.$apply(), it means your$scope.$apply() isn't high enough in the call stack.
  • Don't use jQuery to generate templates or DOM
  • Don't create a new plugin without trying to discover, fork and pull request existing plugins first
  • JQuery performance is a known bottleneck
    •   See http://jsperf.com/performance-fsdfsd, or http://vanilla-js.com/ 
      

References

  1. [The Top 10 Mistakes AngularJS Developers Make] (http://www.airpair.com/angularjs/posts/top-10-mistakes-angularjs-developers-make)
  2. [Opinionated AngularJS styleguide for teams] (http://toddmotto.com/opinionated-angular-js-styleguide-for-teams/?utm_source=javascriptweekly&utm_medium=email)
  3. [Angular Style Guide] (https://github.com/mgechev/angularjs-style-guide)
  4. [Angular Style Guide for Closure Users at Google] (http://google-styleguide.googlecode.com/svn/trunk/angularjs-google-style.html#modules)
  5. [John Papa’s Angular Style Guide] (https://github.com/johnpapa/angularjs-styleguide#naming)
  6. [Angular Best Practice] (https://github.com/angular/angular.js/wiki/Anti-Patterns) and [Anti-Patterns] (https://github.com/angular/angular.js/wiki/Anti-Patterns)
  7. Rootscope broadcast