Adam Yeats

things I learned about angular.js in a rapid development process

We’ve been working on a project at MetaBroadcast recently using Angular.js, which is currently our favoured front end framework. I’m a fan of “the Angular way”; it imposes relatively few opinions on you whilst somewhat sticking to established JavaScript idioms and best practices. It’s a very balanced framework.

What I have discovered, however, is despite having relatively few opinions, there are ways that you can reduce headaches in a development cycle, and there was no better way to discover these than in a fast-paced development cycle where the pressure is on and there’s little margin for error. Some of these tips are very Angular specific, while others are general enough to be applied anywhere on the stack.

test first

As Tim mentioned in his previous blog post, at the pace we were working at it was easy to run into regressions and breaking changes. Even as a reasonably un-opinionated framework, Angular still encourages you to test first, which is frankly what we should have done to prevent regressions and bugs from sneaking into our staging code.

Start with a solid mockup and write at least integration/end-to-end tests to prevent regressions in the UI. This is what your clients will notice the most during the demo or your pitch! Pending specs make a good to-do list of unimplemented functionality (this functionality is available in Mocha, and, to some extent, Jasmine), and don’t be afraid to respond to change – project needs often change on a dime so don’t be afraid of deleting tests and writing new ones.

My personal opinion on testing is that you should always aim for 100% coverage. However, 100% test coverage is an aim, but not a goal; if you can get to 100% coverage then that’s great, but if not, it’s not the end of the world. Generally, alarm bells would start ringing for me at anything below 75-80% coverage overall.

In future, we plan to set up the Karma test runner together with Mocha and the Chai assertion library to test our front end code on multiple devices and browsers, and integrating with our existing Jenkins set-up to create last-known-good builds for staging and production.

controllers are classes

An often misunderstood concept in Angular is the $scope, which is an object that Angular injects into your controller functions. What this is actually for, however, seems to be a much disputed point in the Angular community. What is okay to store on $scope? I must admit that I may myself have been using $scope in ways that are perhaps against established best practice, and we suffered later in the project due to messy code in need of refactoring.

The issue with $scope is that it’s very easy to run into what many are now calling “scope soup”, where controllers are a tangled rat’s nest of code and mixed concerns. Is it where models should be stored? Is it where I should put my methods to manipulate current state? And what happens if I want to use scope inheritance and I want two properties of the same name defined in different controllers? It’s easy enough to really put anything you like inside $scope, and we did. Once you pass a hundred LOC, however, the situation quickly starts to worsen and it soon becomes difficult to see what a particular controller is doing.

The answer could be found in Google’s own Angular style guide. Controllers are, at the heart, classes that are instantiated when the controller is compiled. Using the $routeProvider‘s controllerAs option and putting methods and properties directly onto the controller’s this property, instead of binding models, methods, event handlers etc. all directly to $scope, makes for more idiomatic JavaScript and also gives you the added bonus of being able to target a specific controller within your templates and clearly separate controllers from each other.

interceptors are underrated

Interceptors are an awesome feature of Angular that I didn’t actually know too much about before starting with this project. Within Angular’s internals, there is a way to hook onto the steps taken by Angular before XHR requests are made. This is particularly nice for preflighting requests with certain headers and the like.

In fact, we needed to do exactly this for adding authorisation headers to the requests being made to the API backend. Our solution, in the end, looked roughly like this:


angular.module('myApp.services.auth', []).
factory('authInterceptor', ['$q', '$location', '$injector', function ($q, $location, $injector) {
'use strict';
    return {
        request: function (config) {
            var session = sessionStorage.getItem('user');
            if (session) {
                // add your headers here
                config.headers['Authorization'] = // a value
            }
            return config || $q.when(config);
        },
        responseError: function (rejection) {
            if (rejection.status === 401) {
                $location.path('/signin');
            }
            return $q.reject(rejection);
        },
        response: function (response) {
            return response || $q.when(response);
        }
    };
}]);

everything in its right place

One crucial thing to understand is that Angular has lots of different parts, and it should go without saying that it’s useful to know what these parts should be doing before you get started. This is just a rough guide and your mileage will vary on what you find useful for your use case, but the below are the generally accepted use cases for each part of Angular.

  • Filters are exactly what they sound like. If it filters or formats data in some way, it should be separated into a filter and either used directly in your templates, or injected as a dependency in your controller.
  • Services encapsulate your app’s business logic. A factory is similar to a service, but the difference is subtle. Think of a service as something you would use the new keyword to initialise normally (an “instance” of something), whereas factories should return a value.
  • Directives are where you should handle DOM manipulation. Directives are cool in that they allow for what I call “stateful elements”, by which I mean to say a template or element on your page that has it’s own encapsulated state outside of a controller.

Separate each bit of functionality into a new file in a separate directory for each category in your /js/ directory e.g. /js/directives/formbuilder.js, and namespace the module appropriately, for example: myApp.directives.formbuilder. Don’t forget to concatenate and minify all these separate files before going into production – your users will thank you for all the extra HTTP requests they’re saving on!

use JSHint to maintain a consistent style across developers

It’s a well-known fact that JavaScript has Good Parts and bad parts. It’s important to respect that while coding, particularly across a team of developers. Some may not know the various gotchas that JavaScript can sneak into your codebase if you’re not vigilant enough, whereas others may have that knowledge already but have a slightly different preference in how they like their JavaScript to be written, that might make your code look inconsistent to the reader.

My favoured approach here is to set up a .jshintrc file for distribution amongst team members. JSHint, for those that might not know, is a JavaScipt code linting tool that runs both in the browser and, usefully, as a command line utility (for integrating with Grunt and the like) or even as a Sublime Text 2 plugin that lints your code as you type. A .jshintrc file in the root of your project folder will be picked up by the latter two methods, making this a useful way to share and impose a particular set of stylistic and “best practice” rules that can be packaged within a Git repo.

I set up my JSHint with very strict rules right off the bat and, depending on changes in the project, relax some of those rules as time goes on. To give you an example of when you might need to relax your JSHint rules: our JSHint file was set to warn whenever a variable used snake_case rather than camelCase for variables, which made it kick up a lot of fuss when working with data from our JSON API, which served data back to the client in snake_case. We had to reverse this rule later in the project.

That’s about it from me! Please feel free to leave comments below or tweet me if you have any further comments or corrections. This is definitely not an exhaustive guide to working with Angular, and I’m very interested to hear if anyone has any improvements on what I’ve outlined here.

blog comments powered by Disqus