GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Written by thoughtbot

Avoid AngularJS Dependency Annotation with Rails

In AngularJS, it is a common practice to annotate injected dependencies for controllers, services, directives, etc.

For example:

angular.module('exampleApp', [])
  .controller('ItemsCtrl', ['$scope', '$http', function ($scope, $http) {
    $http.get('items/index.json').success(function(data) {
      $scope.items = data;
    });
  }]);

Notice how the annotation of the injected parameters causes duplication ($scope and $http appears twice). It becomes the responsibility of the developer to ensure that the actual parameters and the annotation are always in sync. If they are not, problems will occur that can cause lost time while head-scratching. As the list of parameters grows, it gets even harder to maintain.

The reason for needing to annotate the injected dependencies is documented in the AngularJS docs under “Dependency Annotation”:

To allow the minifiers to rename the function parameters and still be able to inject right services, the function needs to be annotated…

So, JavaScript minifiers will rename function parameters to something short and ambiguous (usually using just one letter). In that case, AngularJS does not know what service to inject since it tries to match dependencies to the parameter names.

Variable mangling and Rails

When using AngularJS with Rails, developers typically rely on the asset pipeline to handle the minification of JavaScript. Namely, the uglifier gem is the default, and most commonly used. With uglifier we are given an option to disable the mangling of variable names. JavaScript will still be minified – whitespace stripped out and code nicely compacted – but the variable and parameter names will remain the same.

To do this, in your Rails project disable the mangle setting for uglifier in the production (and staging) environment config, like so:

# config/environments/production.rb
ExampleApp::Application.configure do
  ...
  config.assets.js_compressor = Uglifier.new(mangle: false)
  ...
end

With that in place, you can write your AngularJS code without needing to annotate the injected dependencies. The previous code can now be written as:

angular.module('exampleApp', [])
  .controller('ItemsCtrl', function ($scope, $http) {
    $http.get('items/index.json').success(function(data) {
      $scope.items = data;
    });
  });

With this trick, there is no duplication of parameter names and no strange array notation.

The catch

Here are a couple of screenshots that show the difference in HTTP responses between mangling and non-mangling variable names, on a project of about 500 lines of production AngularJS code:

With full uglifier minification (variables are mangled): full-minification-screenshot

With variable mangling disabled (no variable mangling): mangling-disabled-screenshot

Disabling variable name mangling comes at the cost of about 200KB more. The size difference between the two settings can be more significant on larger projects with a lot more JavaScript code. The dilemma is to decide whether the convenience gained during development outweighs the size cost. Keep in mind that HTTP compression of web requests can help reduce the size difference. Benchmarking and comparison is advised on a per project basis.

What’s next?

If you found this useful, you might also enjoy: