In my AngularJS beginner’s guide, I made a mention of wrongly architecting and stringing together my modules. Filters, controllers and everything else were bound directly to the main application. Here’s what that looks like (as a refresher):

var app = angular.module('myapp', ['someDependency']).config();

app.controller('FeedController', function($scope) { });
app.filter('markdown', function() { });

Of course, at least I separated all the components in the correct files. However, it’s not a good idea to bind self-contained modules to its parent application in AngularJS (by referencing directly the application). Before we get into using dependency injection (DI from now on), I’d like to go over the problems of intertwined modules and the advantages of DI.

Sharing is a good thing

Let’s consider some pitfalls of my initial approach:

  • Not only does our application depend on our controllers, filters, etc. (as it should), but our controllers, filters, etc. are now dependent on the application to exist. If I don’t declare app, every module will fail automatically.
  • Reusing modules for a different application will require a monolithic naming scheme (in our case all applications wanting to use our modules would have to be named app)
  • It ruins encapsulation. Our modules are no longer just “modules”, they’re integral parts of the application. And neither can exist without the other.

If you’d like to read more into DI and the service locator pattern (similar to what Angular uses) in general, check out game programming patterns chapter on it.

The idea here is that:

  1. modules are not accessible globally
  2. any module that needs to use another module has to explicitly set it
  3. AngularJS will do DI automatically for you so that you don’t have to write the logic yourself

One more thing, having DI allows us to explicitly set what our module needs to work. When we set that listing, it’s easier to debug our application and we always, immediately, and at a glance know what the module’s needs are for when we move the module to another application or when we’re just refactoring our application. It makes things neater.

And if you’ve been programming for a while, neater is always better for the long term.

How to DI

First, there are several ways Angular let’s us inject module into an application. Implicitly (by listing them as an argument of a function), and explicitly (array notation, there are two ways to do this one, too). These two methods have their pitfalls and advantages and honestly, I’ve used a mix of the two but it’s usually a good idea to stick to one.

Here’s an example of an implicit DI:

app.filter('trust', function($sce) {
        return function(input) {
            return $sce.trustAsHtml(input || '');
        }
});

As you can see, the only place our dependency $sce is listed is as an argument of the filter function. An explicit (array notation) way of doing the same thing would be this:

app.filter('trust', ['$sce', function(security) {
        return function(input) {
            return security.trustAsHtml(input || '');
        }
}]);

The idea here is that you can list all of your dependencies (other modules, built in Angular modules), and name them whatever you want in the function declaration. In my case, I renamed `$sce` to be available as `security` in my filter. The listing order of dependencies and their name counterparts MUST match. So for instance:

app.controller(‘FeedController’, [‘$scope’, ‘feedFactory’, function(scope, factory) {
scope.feed = [];

var init = function() {
    factory.getFeed.success(function(data) {
        $scope.feed = data;
     });

};

init();

scope.newComment = '';
scope.addComment = function(comment, i) {
  scope.feed[i].comments.push(comment);
  scope.newComment = '';
};
}]);

So since we listed $scope first, our first argument will be that. feedFactory is second and so it’ll be the second argument in the function as well. We could easily rename scope to s and feedFactory to just feed without affecting the application, as long as we did it in the function arguments section and not the array listing.

Note While you ARE able to rename your DI variables to whatever you want, the popular convention is to keep the original name of the module. So for example, when injecting $http, you’d still keep the name $http to prevent any confusion.

Refactoring to follow convention

So far so good but that’s not all. If you’ve read my beginner’s guide, you’ll know I left off at Step 7 with my guide, well today, I’m going to go one step further with Step 8 and refactor the application to follow DI conventions. For reference, check out the code for step 7 on github. If you haven’t followed my guide, don’t worry, I’ll go over everything here.

Let’s start with refactoring Controllers. Here’s the before:

app.controller('FeedController', function($scope, feedFactory) {
    $scope.feed = [];

    var init = function() {
        feedFactory.getFeed.success(function(data) {
            $scope.feed = data;
         });

    };

    init();

    $scope.newComment = '';
    $scope.addComment = function(comment, i) {
      $scope.feed[i].comments.push(comment);
      $scope.newComment = '';
    };
});

Right away, you can see we use implicit DI by listing our dependencies as arguments of our function. Angular’s documentation lists a few reasons why this is not the best idea, namely due to minification. Minification has a tendency to rename all our variables into shorter names. By specifying our dependencies as strings, we don’t care that much what the variable name is.

One more other thing we’ll want to change is binding our controllers as their own modules rather than as part of app. This is done like so:

angular.module('namespace.to.inject', []).controller('FeedController', function(){});

It looks a lot like how we declare our application, and you’re right! The argument we use for our module namespace.to.inject is a special name for our controller that we use to inject it as a dependency of something else. In our case, our application depends on our FeedController so our application will have to list namespace.to.inject as a dependency.

Here’s the final result:

angular.module('controllers.feed', []).controller('FeedController', ['$scope', 'feedFactory', function(scope, feeder) {
    scope.feed = [];

    var init = function() {
        feeder.getFeed.success(function(data) {
            scope.feed = data;
         });

    };

    init();

    scope.newComment = '';
    scope.addComment = function(comment, i) {
      scope.feed[i].comments.push(comment);
      scope.newComment = '';
    };
}]);

Cool. As an exercise, try to do the second controller! Note the namespace I used. It’s popular convention to describe the module via dot-delimited attributes. In our case, it’s a controller that works with our feeds. If we had a more complicated application, or even a back-end, we might create a longer namespace like: user.controllers.feed if it’s a controller meant for a feed that only a user sees.

Next, let’s update our factory:

app.factory('feedFactory', function($http) {
    return {
        getFeed: $http.get('/api/feed'),
        getUsers: $http.get('/api/users')
    };
});

Same deal:

angular.module('factory.feed', []).factory('feedFactory', ['$http', function(http) {
    return {
        getFeed: http.get('/api/feed'),
        getUsers: http.get('/api/users')
    }
}]);

For fun, go ahead and update the filters in the application. Or don’t. For our directive declarations, there is no way that I’ve found to inject dependencies straight to the link function:

app.directive('commentForm', function() {
  return {
        restrict: 'A',
        template: '<form class="form-vertical" ng-submit="addComment()">' +
                   '<div class="form-group">' +
                      '<textarea class="fill" ng-model="comment"></textarea>' +
                   '</div>' +
                  '<div class="form-group">' +
                      '<input type="submit" class="button main block">' +
                  '</div>' +
                '</form>',
        scope: {
          onAddComment: '&',
          comment: '='
        },
        replace: true,
        link: function (scope, elem, attrs) {
          scope.comment = '';

          scope.addComment = function(){
            scope.onAddComment(scope.comment);
            scope.comment = '';
          }

        }
      }

});

What you CAN do is inject dependencies into the directive but not the directive link function (in the array notation that is, note that I’m still passing dependencies in via the function arguments). Let’s kill off the unnecessary dependencies though:

        link: function (scope) {
          scope.comment = '';

          scope.addComment = function(){
            scope.onAddComment(scope.comment);
            scope.comment = '';
          }
        }

There. And all together:

angular.module('directive.comment.form', []).directive('commentForm', function() {
  return {
        restrict: 'A',
        template: '<form class="form-vertical" ng-submit="addComment()">' +
                   '<div class="form-group">' +
                      '<textarea class="fill" ng-model="comment"></textarea>' +
                   '</div>' +
                  '<div class="form-group">' +
                      '<input type="submit" class="button main block">' +
                  '</div>' +
                '</form>',
        scope: {
          onAddComment: '&',
          comment: '='
        },
        replace: true,
        link: function (scope, elem, attrs) {
          scope.comment = '';

          scope.addComment = function(){
            scope.onAddComment(scope.comment);
            scope.comment = '';
          }

        }
      }

});

Let but not least, let’s handle the application dependencies that we’ll need to specify at run time:

        var app = angular.module("myapp", ['ngRoute']).config(function($routeProvider) {
        $routeProvider.when('/', {
            templateUrl: 'templates/feed.html',
            controller:'FeedController'
        }).when('/users', {
            templateUrl: 'templates/users.html',
            controller: 'UsersController'
        });
    });

First, let’s convert our config declaration into array notation DI:

.config(['$routeProvider', function(routeProvider) {}]);

Next, let’s add all of our dependencies to the app like so:

    var app = angular.module("myapp", [
            'ngRoute',
            'controllers.feed',
            'controllers.users',
            'directive.comment.form',
            'factory.feed', 
            'filter.markdown',
            'filter.trust'
      ]).config(function($routeProvider) {
        $routeProvider.when('/', {
            templateUrl: 'templates/feed.html',
            controller:'FeedController'
        }).when('/users', {
            templateUrl: 'templates/users.html',
            controller: 'UsersController'
        });
    });

Now you have an application that’s built on top of modules rather than hard-wired parts. What’s great is that if you’re running several angular applications at once, or perhaps you have a backend/front-end, you can specify many of the same dependencies to be injected on both sides!