Skip to content

Latest commit

 

History

History
127 lines (99 loc) · 10.1 KB

File metadata and controls

127 lines (99 loc) · 10.1 KB

Angular Style Guide

While Angular follows best practices for JavaScript, it introduces some unique considerations with regard to Angular-specific concepts such as services, controllers, and $scope variables.

Note: This style guide inherits rules from the JavaScript Style Guide, C-Based Languages Style Guide, and the Global Style Guide. If using ECMAScript 6, the ECMAScript 6 Style Guide should also be honored.

Contents

Identifiers

  • Controllers and factories should be PascalCase (as they represent class constructors); all other object identifiers (e.g., directives, services) should be camelCase
  • Controllers should be suffixed with Controller (e.g., HomeController); do not use Ctrl or some other abbreviation
  • The primary module for an application should be named app; if sub-modules are needed, they should be named app.subModule (e.g., app.admin)
  • Use a (short) namespace prefix for directives to avoid naming conflicts; do not use the ng- or ui- prefixes (these are reserved for Angular and Angular UI respectively)
  • Do not prefix methods or properties in controllers or services with $ (this is reserved for Angular and jQuery)

Formatting

Files

  • Place each module, directive, service, and controller in its own file
  • Separate any centralized source files into Directives, Services, and Controllers directories

Note: For larger applications, organize files according to feature (e.g., Home, Account, Profile) instead of by type (e.g., Directives, Services, Controllers) (source) so that applications can be assembled (or disassembled) using modules as functional blocks

  • When using multiple modules, the folder structure should mirror the module structure
  • Module definitions should occur in the root of a module's folder (e.g., app) and be named with the suffix .Module.js (e.g., app.Module.js); this makes it easy to target them first as part of a concatenation process
  • Module files should only contain the module declaration and any global configuration settings or constants for that module

Note: For larger applications, configuration settings and constants should be broken out into their own files (e.g., app.Config.js, app.Constants.js)

  • Routes should be placed in their own file (e.g., app.Routes.js) and be placed in the root of the application's scripts directory; this provides a convenient "manifest" of controllers

Language Features

  • Only include jQuery if advanced features are needed; Angular includes a "lite" version of jQuery which supports most basic features
    • If jQuery is needed, it must be included before the Angular scripts
  • Do not store modules in a global variable (e.g., var module = angular.module(module, [])); instead, instantiate them using angular.module('module', []) and extend them using angular.module('module')
  • Enclose Angular scripts in an immediately-invoked function expression (IIFE) in order to prevent unnecessary polluting of the global scope
  • Always include dependency injection parameters using the optional array format (e.g., angular.controller('Name', ['$service', function ($service) { ... }])), $inject, or ngAnnotate's /* @ngInject */ annotation; these methods ensure compatibility with code obfuscation (e.g., UglifyJS)
  • Do not use function expressions (e.g., function() {}) for public methods; instead, use function declarations (e.g., function methodName() {}); this allows the public interface to be summarized at the top of a file, with implementation details provided later (source)
  • When chaining multiple promises, consider adding a catch() handler to centralize error handling
  • Only use $broadcast(), $emit() and $on() for events that need to be globally accessible; prefer using $scope.$watch, services, or directive-controllers for internal communication (source)
  • Use $scope.$on('$destroy', ...) for garbage collection on controllers and directives to remove any plugins or listeners in single-page applications

Consider using a thin app module with features being broken out into sub-modules (source), each with their own app manifest (source).

(function() {
  'use strict';

  angular
    .module('app', [])
    .factory('bookService', bookService)

  bookService.$inject = ['$resource'];

  function bookService($resource) {

    var service = {
      this.total = 0;
      this.getBooks = getBooks;
    }

    return service;

    function getBooks(authorName) {
      ...
    }

  }

}());

Data Binding

  • Prefer ng-bind (or ng-cloak) to {{interpolation}} in views to ensure that binding expressions are not temporarily displayed while loading the Angular controller
  • Always use the ng-src, ng-href, or ng-style directives when binding dynamic content to images, links, or styled elements respectively
  • If data-bound attributes do not need to be updated after the initial digest cycle, use the one-time binding syntax (e.g., ::property); this improves performance
  • For performance reasons, avoid using $watch unless absolutely necessary
    • For collections, prefer $watchCollection unless deep watching is required by the application
    • For very large collections, consider using angular-immutable

Controllers and Routes

  • Centralize reusable logic in services; controllers should only contain minimal view-specific logic
  • When using ng-route, prefer controllerAs to relying on $scope (this allows scope variables to be assigned to this)
    • Use a consistent object name for ControllerAs (e.g., model), and assign it to this within controllers; e.g., var model = this;
  • Wrap intialization logic in an init() method within controllers to keep logic centralized and simplify refresh requirements (source)
  • Use AngularUI Router for routing, as it supports nested views (if needed)

Services

  • Use factories instead of services: the syntax is similar, but factories simplify separation of implementation details; if centralized configuration is needed, then use a provider
  • Define the returnable object at the top of a factory or directive to establish its public interface; all methods should point to function declarations after the returned object (see code sample above)
  • If a service method executes a promise, prefer returning the promise to the value so it can be chained by the caller
  • To institute global custom exception handling, decorate the $exceptionHandler service using the $provide service (source)
  • Reusable blocks (e.g., exception handling, logging, security, and local data storage) needed across multiple applications should be stored in their own modules (source)
  • Use the $cacheFactory service to provide session-based caching of expensive operations; note that this is handled automatically by $http and $resource

Directives

  • Do not provide DOM manipulation from a controller; use directives instead (e.g., existing directives such as ng-show or custom directives)
  • Prefer assigning directives to elements (restrict: 'E') and optionally attributes (restrict: 'A') over classes (restrict: 'S') (source)
    • Prefer instantiating directives as elements if they are acting as a control (e.g., ui-date-picker), and attributes for modifiers (e.g., ng-model)
    • If HTML validation is critical, define all directives as elements prefixed with data- (e.g., data-ng-model)
  • When using Bootstrap, use the AngularUI Bootstrap directives; this provides more succinct coverage of Bootstrap markup and classes
  • Always use the $sce service and, if appropriate, the ngSanitize module when relying on user inputted markup (these are accounted for by default in first-party directives, such as ngBindHtml)
  • Use the $observe() method to ensure expressions can be effectively used in attribute values

Acknowledgments