Sunday, 15 May 2016

Agular code style Guide

Table of Contents
§  Modules
§  Services
§  Factories
§  Comments
§  JSHint
§  Constants
Single Responsibility
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.
Immediately Invoked Function Expressions (IIFE)
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.
Example
/* recommended */
// logger.js
(function () {
   angular
   .module('app')
   .factory('logger', logger);

   function logger () { }
})();

// storage.js
(function () {
   angular
   .module('app')
   .factory('storage', storage);

   function storage () { }
})();
 Additional information
Remarks
For brevity only, the rest of the examples in this guide may omit the IIFE syntax.
Modules
Definitions (aka Setters)
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.
Example
/* avoid */
var app = angular.module('app', [
  'ngAnimate',
  'ngRoute',
  'app.shared'
  'app.dashboard'
]);


/* recommended */
angular
   .module('app', [
     'ngAnimate',
     'ngRoute',
     'app.shared'
     'app.dashboard'
   ]);
Getters
When using a module, using chaining with the getter syntax produces more readable code and avoids variables collisions or leaks.
Example
/* avoid */
var app = angular.module('app');
app.controller('SomeController' , SomeController);

function SomeController() { }


/* recommended */
angular
   .module('app')
   .controller('SomeController' , SomeController);

function SomeController() { }
Setting vs Getting
A module should only be created once. Use angular.module('app', []); to set a module and angular.module('app'); to get a module.
Named vs Anonymous Functions
Using named functions for callbacks produces more readable code, is much easier to debug, and reduces the amount of nested callback code.
Example
/* avoid */
angular
   .module('app')
   .controller('Dashboard', function () { });
   .factory('logger', function () { });


/* recommended */
// dashboard.js
angular
   .module('app')
   .controller('Dashboard', Dashboard);

function Dashboard () { }

// logger.js
angular
   .module('app')
   .factory('logger', logger);

function logger () { }
Controllers
controllerAs syntax in the view
Use the controllerAs syntax over the classic controller with $scope syntax. This syntax is closer to that of a JavaScript constructor and it promotes the use of binding to a "dotted" object in the View (e.g. 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.
Example
<!-- avoid -->
<div ng-controller="Customer">
   {{ name }}
</div>


<!-- recommended -->
<div ng-controller="Customer as customer">
   {{ customer.name }}
</div>
controllerAs syntax in the controller
Using the 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 $emit$broadcast, or $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 $scope methods.
Example
/* avoid */
function Customer ($scope) {
   $scope.name = {};
   $scope.sendMessage = function () { };
}


/* recommended - but see next section */
function Customer () {
   this.name = {};
   this.sendMessage = function () { };
}
controllerAs with the view model
The 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.
Example
/* avoid */
function Customer () {
   this.name = {};
   this.sendMessage = function () { };
}


/* recommended */
function Customer () {
   /* jshint validthis: true */
   var vm = this;
   vm.name = {};
   vm.sendMessage = function () { };
}
 Note
The /* jshint validthis: true */ comment avoids any jshint warnings.
Bindable Members Up Top
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.
Example
/* avoid */
function Sessions() {
   var vm = this;

   vm.gotoSession = function() {
      /* ... */
   };
   vm.refresh = function() {
      /* ... */
   };
   vm.search = function() {
      /* ... */
   };
   vm.sessions = [];
   vm.title = 'Sessions';
}


/* recommended */
function Sessions() {
   var vm = this;

   vm.gotoSession = gotoSession;
   vm.refresh = refresh;
   vm.search = search;
   vm.sessions = [];
   vm.title = 'Sessions';

   function gotoSession() {
      /* */
   }

   function refresh() {
      /* */
   }

   function search() {
      /* */
   }
}
Defer Controller Logic
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.
Example
/* avoid */
function Order ($http, $q) {
   var vm = this;
   vm.checkCredit = checkCredit;
   vm.total = 0;

   function checkCredit () {
      var orderTotal = vm.total;
      return $http.get('api/creditcheck').then(function (data) {
         var remaining = data.remaining;
         return $q.when(!!(remaining > orderTotal));
      });
   };
}


/* recommended */
function Order (creditService) {
   var vm = this;
   vm.checkCredit = checkCredit;
   vm.total = 0;

   function checkCredit () {
      return creditService.check();
   };
}
Assigning Controllers
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.
 Note
If a View is loaded via another means besides a route, then use the ng-controller="Avengers as vm" syntax.
Example
// route-config.js
angular
.module('app')
.config(config);

function config ($routeProvider) {
$routeProvider
  .when('/avengers', {
    templateUrl: 'avengers.html',
    controller: 'Avengers',
    controllerAs: 'vm'
  });
}
Example
<!-- avengers.html -->
<div>
</div>
 Additional information
See also
Services
All AngularJS services are singletons, which means that there is only one instance of a given service per injector. Services are instantiated with the newkeyword. Use this for public methods and variables. You can also use a factory instead of a service, which can introduce greater consistency.
Example
// service
angular
   .module('app')
   .service('logger', logger);

function logger () {
   this.logError = function (msg) {
      /* */
   };
}


// factory
angular
  .module('app')
  .factory('logger', logger);

function logger () {
   return {
      logError: function (msg) {
         /* */
      }
   };
}
Factories
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).
Example
function dataService () {
   var someValue = '';
   var service = {
      save: save,
      someValue: someValue,
      validate: validate
   };
   return service;

   ////////////
   function save () {
      /* */
   };

   function validate () {
      /* */
   };
}
 Additional information
See also
Directives
One directive per file
Creating one directive per file, with the name the file matching the directive, makes them easier to maintain.
Example
/**
* @desc order directive that is specific to the order module at a company named Acme
* @file calendarRange.directive.js
* @example <div acme-order-calendar-range></div>
*/
angular
   .module('sales.order')
   .directive('acmeOrderCalendarRange', orderCalendarRange)

/**
* @desc spinner directive that can be used anywhere across the sales app at a company named Acme
* @file customerInfo.directive.js
* @example <div acme-sales-customer-info></div>
*/   
angular
   .module('sales.widgets')
   .directive('acmeSalesCustomerInfo', salesCustomerInfo);

/**
* @desc spinner directive that can be used anywhere across apps at a company named Acme
* @file spinner.directive.js
* @example <div acme-shared-spinner></div>
*/
angular
   .module('shared.widgets')
   .directive('acmeSharedSpinner', sharedSpinner);
Limit DOM Manipulation
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, ngShow or ngHide, use those instead.
Restrict to Elements and Attributes
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.
Example
<!-- recommended -->
<my-calendar-range></my-calendar-range>
<div my-calendar-range></div>
Example
/* recommended */
angular
   .module('app.widgets')
   .directive('myCalendarRange', myCalendarRange);

function myCalendarRange () {
   var directive = {
      link: link,
      templateUrl: '/template/is/located/here.html',
      restrict: 'EA'
   };
   return directive;

   function link(scope, element, attrs) {
      /* */
   }
}
 Additional information
See also
Resolving Promises for a Controller
Controller Activation Promises
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 activate function.
Example
function Avengers(dataservice) {
   var vm = this;
   vm.avengers = [];
   vm.title = 'Avengers';

   activate();

   ////////////

   function activate() {
      return dataservice.getAvengers().then(function(data) {
         vm.avengers = data;
         return vm.avengers;
      });
   }
}
Route Resolve Promises
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.
Example
// route-config.js
angular
   .module('app')
   .config(config);

function config ($routeProvider) {
   $routeProvider
      .when('/avengers', {
         templateUrl: 'avengers.html',
         controller: 'Avengers',
         controllerAs: 'vm',
         resolve: {
            moviesPrepService: function (movieService) {
               return movieService.getMovies();
            }
         }
      });
}

// avengers.js
angular
   .module('app')
   .controller('Avengers', Avengers);

function Avengers (moviesPrepService) {
   var vm = this;
   vm.movies = moviesPrepService.movies;
}
 Note
If you need to conditionally cancel the route before you start using the controller or before it's activated, use a route resolver instead.
 Additional information
See also
Manual Dependency Injection
To prevent parameters from being converted to mangled variables, avoid using the shortcut syntax of declaring dependencies without using a minification-safe approach.
Using $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.
Example
angular
   .module('app')
   .controller('Dashboard', Dashboard);

Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice'];

function Dashboard($location, $routeParams, common, dataservice) {
}
 Additional information
See also
Minification and Annotation
Using 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.
 Note
Starting from AngularJS 1.3 use the ngStrictDi parameter on the ng-app directive: 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.
 Additional information
See also
Exception Handling
To provide a consistent manner for customizing how exeptions are handled, use a decorator at configuration time using the $provide service on the$exceptionHandler service to perform custom actions when exceptions occur.
Example
angular
   .module('app.exception')
   .config(['$provide', exceptionConfig]);

function exceptionConfig($provide) {
   $provide.decorator('$exceptionHandler', ['$delegate', '$log', extendExceptionHandler]);
}

function extendExceptionHandler($delegate, $log) {
   return function (exception, cause) {
      $delegate(exception, cause);
      var errorData = {
         exception: exception,
         cause: cause
      };
      var msg = 'ERROR PREFIX' + exception.message;
      $log.error(msg, errorData);

      // Log during dev with http://toastrjs.com
      // or any other technique you prefer
      toastr.error(msg);
   };
}
Application Structure
Follow the LIFT Principle
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
§  Keep the Flattest structure you can
§  Try to follow the Don't Repeat Yourself (DRY) pattern
Angular $ Wrapper Services
Use $document and $window instead of document and window. They are more easily testable than using document and window.
Use $timeout and $interval instead of setTimeout and setInterval. They are more easily testable and handle AngularJS's digest cycle, thereby keeping data binding in sync.
Comments
Using 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.
Example
angular
   .module('app')
   .factory('logger', logger);

/**
* @name logger
* @desc Application wide logger
*/
function logger ($log) {
   var service = {
   logError: logError
   };
   return service;

   ////////////

   /**
   * @name logError
   * @desc Logs errors
   * @param {String} msg Message to log
   * @returns {String}
   */
   function logError(msg) {
      var loggedMsg = 'Error: ' + msg;
      $log.error(loggedMsg);
      return loggedMsg;
   };
}
JSHint
Using JS Hint for linting your JavaScript provides a first alert prior to commiting any code changes and helps promote consistency across the team. Be sure to customize the JS Hint options file and include it in source control. An example .jshintrc file is shown below.
Example
{
   "bitwise": true,
   "camelcase": true,
   "curly": true,
   "eqeqeq": true,
   "es3": false,
   "forin": true,
   "freeze": true,
   "immed": true,
   "indent": 4,
   "latedef": "nofunc",
   "newcap": true,
   "noarg": true,
   "noempty": true,
   "nonbsp": true,
   "nonew": true,
   "plusplus": false,
   "quotmark": "single",
   "undef": true,
   "unused": false,
   "strict": false,
   "maxparams": 10,
   "maxdepth": 5,
   "maxstatements": 40,
   "maxcomplexity": 8,
   "maxlen": 120,
   "asi": false,
   "boss": false,
   "debug": false,
   "eqnull": true,
   "esnext": false,
   "evil": false,
   "expr": false,
   "funcscope": false,
   "globalstrict": false,
   "iterator": false,
   "lastsemic": false,
   "laxbreak": false,
   "laxcomma": false,
   "loopfunc": true,
   "maxerr": false,
   "moz": false,
   "multistr": false,
   "notypeof": false,
   "proto": false,
   "scripturl": false,
   "shadow": false,
   "sub": true,
   "supernew": false,
   "validthis": false,
   "noyield": false,
   "browser": true,
   "node": true,
   "globals": {
       "angular": false,
       "$": false
   }
}
 Additional information
See also
Constants
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).
Example
// constants.js

/* global toastr:false, moment:false */
(function () {
   'use strict';

   angular
      .module('app.core')
      .constant('toastr', toastr)
      .constant('moment', moment);
})();


2 comments:

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...