Angular Dependency Injection. And Dolphins

Have you ever found yourself asking: should I use a value? a factory? a service? a provider?!

Rules of thumb

Ask yourself 3 questions:

  1. Do I need to inject it in the config phase?
  2. Does it have dependencies?
  3. Am I a dolphin?

Then reduce which type of service you need to use:

  • Service     <=  not needed in config phase, has dependencies
  • Value        <=  not needed in config phase, has no dependencies
  • Constant  <=  needed in config phase, has no dependencies
  • Provider   <=  needed in config phase, has dependencies
  • Factory     <=  You are a dolphin

Notice that if you answer yes to the third question you don't need to ask yourself the other questions - optimizing the algorithm :)

The reasoning behind

Config phase

Angular modules have a config phase, that runs before the app is bootstrap. During that phase you can only inject constants or providers. You cannot inject a service or a value:

angular.module('app', [])  
.config(function(Constant, Provider) {
  // ...
})
.controller(function(Service, Value, ...) {
})
;

Dependencies

Constants and values can't inject dependencies. Thus, they are simpler to define:

angular.module('app', [])  
  .constant('PI', Math.PI)
  .value('Temperature', 27.5)

Why use value?
In most cases, there's no real justification for using a value over a constant.
The main advantage I found, is that you can override it in tests, in order to mock it.

Why not factory?
To understand why you never need to use a factory, you need to know how the injector instantiates our services.

For a service, it's something like this:

service = new Service(dependency1, dependency2, ...)  

For a factory, it's something like this:

factory = Factory(dependency1, dependency2, ...)  

In Javascript a constructor function can ignore the fact that it was called with new operator, and act as a regular function, returning whatever it likes.
For example:

function Service() {  
  return { value: 3 };
}
var service = new Service();  
// service.value === 3;

function AnotherService() {  
  this.value = 3;
}
var anotherService = new AnotherService();  
// anotherService.value === 3;

However, if it hasn't been called with new, you can't use it as a constructor, but you have to return a value. For example:

function Factory() {  
  this.value = 3;
}
factory = Factory();  
// factory === undefined;

Thus, you can replace all your factories with services, and everything will work the same way, but not the other way around.

*disclaimer:
There's only thing you can't return from a constructor function invoked with new is a primitive value (string, integer, etc.). So the only case it will make sense to use a factory is for defining primitive values that require dependencies.

Hope you enjoyed the guide!