---
title: Avoid AngularJS Dependency Annotation with Rails
teaser: 'Annotating AngularJS inject dependencies got you down? Use automated tools
  to annotate for you.

  '
tags: web,javascript,angularjs,rails
author: Greg Lazarev
published_on: 2014-08-07
---

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

For example:

```javascript
(function() {
  'use strict';

  angular.module('exampleApp', [])
    .directive('myDirective', ['$location', 'myService', myDirective]);

  function myDirective($location, myService) {
    return {
      scope: {},
      link: link,
    };

    function link(scope, element, attrs) {
      myService.doSomething($location.path());
      // ...
    }
  }
})();
```

Notice how the annotation of the injected arguments causes duplication
(`$location` and `$myService` appears twice).
It becomes the responsibility of the developer to ensure that
the actual arguments 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 arguments grows, it gets even harder to
maintain.

The reason for needing to annotate the injected dependencies is documented in
the [AngularJS docs][angular-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...

JavaScript minifiers will rename function arguments and 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 argument names.

## ng-annotate and ngannotate-rails

[`ng-annotate`][ng-annotate] is a tool that
automatically adds AngularJS dependency injection annotations.
It is non-intrusive and sends annotated results to the minifier.
It handles rebuilding annotations, adding and removing them as needed.

[`ngannotate-rails`][ngannotate-rails] is a Ruby gem that wraps `ng-annotate`
and seamlessly integrates it into Rails' asset pipeline.
Both, JavaScript and CoffeeScript, are supported.
Using it is as easy as adding it to the Gemfile and bundling.

Once `ngannotate-rails` is in place,
AngularJS code can be written without needing to annotate
the injected dependencies.
The previous code can now be written as:

```javascript
(function() {
  'use strict';

  angular.module('exampleApp', []).directive('myDirective', myDirective);

  function myDirective($location, myService) {
    return {
      scope: {},
      link: link,
    };

    function link(scope, element, attrs) {
      myService.doSomething($location.path());
      // ...
    }
  }
})();
```

Now there is no duplication of argument names and no weird array notation.

## Manual dependency annotation

95% of the time `ngannotate-rails` will handle annotating the dependencies,
but there are situations where dependencies cannot be derived using static
analysis. In such a situation, manual annotation is required.

John Papa's wonderful [community styleguide][styleguide] suggests the following:

> Use $inject to manually identify your dependencies for Angular components.

This is recommended because it's the same technique that `ng-annotate` uses.
In addition, it has the benefit of being a straight-forward assignment,
rather than an unusual array notation.

```javasript
(function() {
  'use strict';

  angular.module('exampleApp', []).directive('myDirective', myDirective);

  function item($location, myService) {
    return {
      scope: {},
      link: link,
      controller: controller,
    };

    function link(scope, element, attrs) {
      myService.doSomething($location.path());
      // ...
    }

    controller.$inject = ["$scope"];
    function controller($scope) {
      $scope.foo = "bar";
      // ...
    }
  }
})();
```

In this case, `myService` will be automatically annotated by `ngannotate-rails`,
but `$scope` that is passed into the `controller` will not be.
Thus, it must be manually annotated via `controller.$inject = ["$scope"]`.

Keep in mind these situations and consider checking for any missing injection
annotations next time you get an [`Unknown Provider`][unknown-provider] error in
your application.

## What's next

If you found this useful, you might also enjoy:

* [Preload Resource Data into AngularJS][preload-resource]
* [Animating Modals in Angular.js][animating-modals]
* [Using JavaScript Promises to Reason About User Interaction][js-promises]

[angular-docs]: https://docs.angularjs.org/guide/di
[ng-annotate]: https://github.com/olov/ng-annotate
[ngannotate-rails]: https://github.com/kikonen/ngannotate-rails
[styleguide]: https://github.com/johnpapa/angular-styleguide#manually-identify-dependencies
[unknown-provider]: https://docs.angularjs.org/error/$injector/unpr

[preload-resource]: https://thoughtbot.com/blog/preload-resource-data-into-angularjs
[animating-modals]: https://thoughtbot.com/blog/animating-modals-in-angular-js
[js-promises]: https://thoughtbot.com/blog/using-javascript-promises-to-reason-about-user-interaction
