AngularJS Style Guides

Ryan Tucker

@rctucker88

Why Style Guides Matter

  • Consistency
  • Clear Communication
  • Teamwork

Single Responsibility

// File1.js

angular
    .module('app', ['ngRoute'])
    .controller('SomeController', SomeController)
    .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

All in one file

// File1.js

angular
    .module('app')
    .controller('SomeController', SomeController);

function SomeController() { }
// File2.js

angular
    .module('app')
    .factory('someFactory', someFactory);

function someFactory() { }

Versus

Split into multiple files

// module.js

angular
    .module('app', ['ngRoute']);

IIFE

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

// logger function is added as a global variable
function logger() { }

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

// storage function is added as a global variable
function storage() { }

Avoid This

// logger.js
(function() {
    'use strict';

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

    function logger() { }
})();

// storage.js
(function() {
    'use strict';

    angular
        .module('app')
        .factory('storage', storage);

    function storage() { }
})();

This is better, no globals left behind!

Naming Collisions

Use a unique naming convention for your module names!  Separators are commonly used like app.users and app.login.

angular.module('myApp', ['app.users', 'app.login', 'app.items']);

Module Getters/Setters

Don't use a variable for a module. Since you only have one component per file anyway this shouldn't be an issue!

/* Avoid this! */
var app = angular.module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared',
    'app.dashboard'
]);

Use a chaining getter syntax instead.

angular
    .module('app')
    .controller('SomeController', SomeController);

function SomeController() { }

Named vs Anonymous Functions

Use named functioned over anonymous functions.

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

function Dashboard() { }
// logger.js
angular
    .module('app')
    .factory('logger', logger);

function logger() { }

Named Functions

Why? More readable code, easier to debug, and reduces nested callbacks.

controllerAs Syntax

Benefits in both the view and the controller.

View Benefits

  • Makes your controllers closer to a JavaScript constructor
  • Promotes using binding to a "dotted" object in the view
  • Helps avoid $parent calls
<!-- avoid -->
<div ng-controller="Customer">
    {{ name }}
</div>

<!-- recommended -->
<div ng-controller="Customer as customer">
    {{ customer.name }}
</div>

Even Better

Capture the 'this' variable with a consistent variable name such as vm.

/* recommended */
function Customer() {
    var vm = this;
    vm.name = {};
    vm.sendMessage = function() { };
}
/* avoid */
function Customer() {
    this.name = {};
    this.sendMessage = function() { };
}

Controller Benefits

  • Uses 'this' inside of controllers instead of the class $scope syntax
  • Avoids temptation to use $scope methods which aren't always the best.
/* avoid */
function Customer($scope) {
    $scope.name = {};
    $scope.sendMessage = function() { };
}
/* Recommended */
function Customer() {
    this.name = {};
    this.sendMessage = function() { };
}

Formatting Your Controller

/* 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() {
      /* */
    }

Why is this helpful?

  • Quickly see what your controller does by looking at the top
  • More readable code
  • Using function declarations avoids worrying about hoisting problems

Defer Controller Logic

Delegate your logic to a service where possible!

 

  • Allows for logic reuse
  • Logic can be more easily isolated for a unit test in a service
  • Removes dependencies and implementation details from the controller

Keep Controllers Focused

Design a controller for a view, and try not to reuse it for other views.

Assigning a Controller

Assign the controller in your router and keep the logic out of the template!

// route-config.js
angular
    .module('app')
    .config(config);

function config($routeProvider) {
    $routeProvider
        .when('/avengers', {
            templateUrl: 'avengers.html',
            controller: 'Avengers',
            controllerAs: 'vm'
        });
}
<!-- avengers.html -->
<div>
</div>

Factories

  • Use factories only
  • Single responsibility
  • Accessible members at the top
  • Don't use anonymous functions for the factory's members

Directives

  • One directive per file
  • Only manipulate the DOM in a directive
  • Provide a unique directive prefix
  • Restrict to element and attributes

Minification and Annotation

  • Always use minification syntax
  • Use ng-annotate to automate minification
  • Use Gulp or Grunt to further automate minification

Credits

Big shout-out to John Papa <@John_Papa> who organizes a large Angular style guide which this presentation is based on.

 

Find the style guide at https://github.com/johnpapa/angularjs-styleguide.

Questions?

Feel free to ask me questions on Twitter: @RCTucker88

AngularJS Style Guides

By Ryan Tucker

AngularJS Style Guides

  • 1,831