Wednesday, July 10, 2013

Angular injection

tl;dr is marked throughout by ∴

I don't like magical code. AngularJS is magical. I must fix this.

Dependency injection was one of AngularJS's first evil magicks I encountered. The idea that calling this function

function myFunction($scope, $http) {
will magically reach out to the universe and grab the correct values for $scope and $http runs contrary to all the JavaScript I've ever used. You can't do that!

So I dug in to discover the magicks. And now it's not magic! It's great! It's rougly equivalent to import in Python or require in Ruby. Here's how it works:


AngularJS groups injectable things together into modules. The following code will:

  1. make a module named woods
  2. add a provider to the woods module named Eeyore, which has a constant value
var woods = angular.module('woods', []);
woods.value('Eeyore', 'sad')

Here's some of the source for the module function plus context (see the full source here — the comments are helpful):

// from setupModuleLoader()
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());

// ...

var modules = {};
return function module(name, requires, configFn) {
// ...
return ensure(modules, name, function() {
// ...
var moduleInstance = {
// ...
requires: requires,
name: name,
provider: invokeLater('$provide', 'provider'),
factory: invokeLater('$provide', 'factory'),
service: invokeLater('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
filter: invokeLater('$filterProvider', 'register'),
controller: invokeLater('$controllerProvider', 'register'),
directive: invokeLater('$compileProvider', 'directive'),
// ...
// ...
return moduleInstance;
// ...

  1. The ensure(obj, name, factory) function makes sure that obj has an attribute named name, creating it by calling factory if it doesn't.
  2. The module(name, requires, configFn) function adds a moduleInstance named name to the global-ish modules object (by using ensure).

angular.module(...) adds a module to some global-ish module registry.


Injectors find providers from among the modules it knows about. By default, AngularJS creates an injector through the bootstrapping process. We can also make an injector with angular.injector() and use it to access providers within modules:

// Run this in a JavaScript console (on a page that has AngularJS)

// Make a woods module with an Eeyore provider
var woods = angular.module('woods', []);
woods.value('Eeyore', 'sad')

// Make an injector that knows about the 'woods' module.
var injector = angular.injector(['woods'])

// Get poor Eeyore out of the module
// -> "sad"

The creation of injectors and how they know where things are is somewhat recursive (and the code is a little hard to read). I will unravel that magic in another post as it was making this post too long. For now, just know that

Injectors can find the providers you add to modules (e.g. through .value(...) or .factory(...)) and can find modules that were previously added to the global-ish module registry.


Using an injector, we can invoke functions with dependency injection:

// Run this in a JavaScript console (on a page that has AngularJS)

// Make a woods module with an Eeyore provider
var woods = angular.module('woods', []);
woods.value('Eeyore', 'sad')

// Make an injector that knows about the 'woods' module.
var injector = angular.injector(['woods'])

// Imbue a function with sadness
function eatEmotion(Eeyore) {
return 'I am ' + Eeyore;
// -> "I am sad"

But how does it KNOOooooowwwWWW??

How does AngularJS know the names of the arguments a function is expecting? How does it know that my weather function's arguments is named sunny?

function weather(sunny) {

That's an internal detail of weather, inaccessible from the outside, no? I've done introspection with Python, but this is JavaScript.

How AngularJS gets the argument names made me laugh out loud when I found it. It's a dirty (effective) trick found in the annontate function (full source):

var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function annotate(fn) {
var $inject,

if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
fn.$inject = $inject;
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
return $inject;

If you pass a function to annotate it will convert that function to a string and use regular expressions to get the names of the arguments.

I should note, however, that the practice of depending on argument names for injection is discouraged (because of how the names get munged during minification). It makes the code look cleaner, though. Maybe we should work on changing minification to handle this introspective kind of injection.

Which functions have it? Which don't?

When you're just starting with AngularJS, it's a little frustrating that some functions are magic (i.e. are called with injection) and some are seemingly inert. For instance, when writing a directive, link is not called with dependency injection, but controller is.

The provider methods are called with injection (factory, value, etc...). And directive controllers are called with injection. From the official docs:

DI is pervasive throughout Angular. It is typically used in controllers and factory methods.

Sadly, the only way to know if a function is called with dependency injection is to... know. Read the docs or the source, and build up an ample supply of doing it wrong :)


Modules provided to an injector will stomp on each other's providers:

// Run this in a JavaScript console (on a page that has AngularJS)

function mineFor(Thing) {
return "I found " + Thing + "!";

// Make two modules that each define a Thing provider
var good_module = angular.module('good', []);
good_module.value('Thing', 'gold');

var bad_module = angular.module('bad', []);
bad_module.value('Thing', 'sour milk');

// Make an injector
var injector = angular.injector(['good', 'bad']);

// -> "I found sour milk!"

I don't know if this is by design or if there are plans to address it. Be aware of it.

In summary

Dependency injection in AngularJS is roughly equivalent to other languages' including and importing, but scoped to functions. Some of the magic is accomplished by exploiting function.toString() and regular expressions.

Read the official doc about Dependency Injection for some of the motivation behind its use.


  1. As to which methods have DI - the thinkster videos make note of something useful: By convention, DI methods have the argument '$scope', where a non-DI method would use 'scope':

  2. As to which methods have DI - the thinkster videos make note of something useful: By convention, DI methods have the argument '$scope', where a non-DI method would use 'scope':

  3. Thanks for the article. Please update it to Angular 4.

    Trainer @ Kamal Techologies - Best Angular training institute

  4. really Good blog post.provided a helpful information.I hope that you will post more updates like this
    Ruby on Rails Online Training

  5. Great efforts put it to find the list of articles. thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.
    Angular JS Training in Chennai | Angular JS Training in Velachery | Angular JS Training in OMR

  6. Each department of CAD have specific programmes which, while completed could provide you with a recognisable qualification that could assist you get a job in anything design enterprise which you would really like.

    AutoCAD training in Noida

    AutoCAD training institute in Noida

    Best AutoCAD training institute in Noida

  7. Thanks for sharing content and such nice information for me. I hope you will share some more content about. angular injection Please keeps sharing!


  8. Good job in presenting the correct content with the clear explanation. The content looks real with valid information. Good Work

    devops training in chennai | devops training in chennai with placement

  9. Thanks for sharing such a wonderful blog on Machine learning.This blog contains so much data about Machine learning ,like if anyone who is searching for the Machine learning data will easily grab the knowledge of Machine learning from this .Requested you to please keep sharing these type of useful content so that other can get benefit from your shared content.
    Thanks and Regards,
    Top institutes for machine learning in chennai
    best machine learning institute in chennai
    artificial intelligence and machine learning course in chennai

  10. Thank you for sharing your article. Great efforts put it to find the list of articles which is very useful to know, Definitely will share the same to other forums.

    best openstack training in chennai | openstack course fees in chennai | openstack certification in chennai | openstack training in chennai velachery

  11. One of the best content i have found on internet for Data Science training in Chennai .Every point for Data Science training in Chennai is explained in so detail,So its very easy to catch the content for Data Science training in Chennai .keep sharing more contents for Trending Technologies and also updating this content for Data Science and keep helping others.
    Cheers !
    Thanks and regards ,
    Data Science course in Velachery
    Data Scientists course in chennai
    Best Data Science course in chennai
    Top data science institute in chennai

  12. This is very nice information, Thank you so much for sharing your knowledge. Keep sharing
    Dot Net Training Institute in Noida
    Oracle Training Institutes in Noida