AngularJS Providers under the hood

AngularJS
Reading Time: 4 minutes

When I started working with Angular, the concept of Angular Provider was confusing to me, especially since all the providers (Factory, Service Value and Provider) seem to perform the same work.

The first question you may ask yourself is "which one you should use?" You'll probably end up picking one, and just stick with it until it no longer helps you do the work you're aiming to do. Which is fine.

Let's say that the only thing you need to know is that all the providers come from the provider recipe and all the five different ways of creating a provider (value, factory, service, provider and constant) at the end come from the same implementation.

Confused? Let me explain this better: it is syntax sugar.

In this blogpost, I'll only talk about the differences between Provider, Service and Factory.

For most cases, it doesn't matter which one you are using, but if you are looking for a deeper answer, let's review each one and discover what are the differences.

Let's start by taking a look at the source code:

https://github.com/angular/angular.js/blob/master/src/auto/injector.js

This class is amazing! It is the definition of $injector, which does all the magic behind dependecy injection in Angular. We are not going to explain how $injector works and all the magic inside this file: we will just take a look at the most important parts that make the following code to work:

var myApp = angular.module('myApp', []);

myApp.factory('myFactory', function(){ 
    return { 
        hello: function() { 
            return "Hello World"; 
        }
    } 
});

myApp.service('myService', function(){ 
    this.hello = function() { 
        return "Hello World"; 
    }; 
}); 

myApp.provider('myProvider', function() {
    this.$get = function() {
        return {
            hello: function() { 
                return "Hello World"; 
            }
        }
    };
});

myApp.controller('myController', function ($scope, myFactory, myService, myProvider) {
    $scope.hellos = [
        myFactory.hello(),
        myService.hello(),
        myProvider.hello()
    ];
});

As you may notice, basically all of them do the same work. If you use them, you won't notice the difference so, why do they exist? what is the real difference here? Well, let's find it out.

** Note: All providers are singletons, the injector only creates them once and caches them for future reference. **

Factories

Let's start with factories. It is important to know the implementation came from a factory pattern and, as you may know, it helps you to create objects: basically, it has a generic object where you create other objects from.

If we check the code we used to create our factory:

myApp.factory('myFactory', function(){ 
    return { 
        hello: function() { 
            return "Hello World"; 
        }
    } 
});

and the Angular constructor method:

function factory(name, factoryFn, enforce) {
  return provider(name, {
    $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
  });
}

You can see there is nothing special: under the hood, factory returns a provider. The advantage is that you can design classes easier and have private methods. This allows you to isolate your logic from the rest of the code, and still have all the benefits of a factory pattern.

Note: Factory and Service use the provider implementation internally, which require a $get method implementation to work, you can see it here

Service

Services are the easiest provider to understand: they behave like a regular class and use the constructor pattern.

By checking the implementation again:

myApp.service('myService', function(){ 
    this.hello = function() { 
        return "Hello World"; 
    }; 
});

And the source code:

function service(name, constructor) {
  return factory(name, ['$injector', function($injector) {
    return $injector.instantiate(constructor);
  }]);
}

you will see that the main difference is that we are not returning any object, but we are creating a new instance of the prototype of our function; as you can see here. Due to this, it is really important to attach your methods to this reference, otherwise, you won't be able to reach the methods from the new instance.

And again, you may notice how Angular uses a factory call, which internally uses a provider to create a new service.

Provider

And finally, we get to the core of all provider recipes: this is the service called provider which give us more flexibility, but is often overwhelming for most cases.

If we already have Service and Factory, when should we use this provider? Providers are the only Angular services that you can configure before getting initialized. You will be able to change some variables from myApp.config and change the behavior.

Let's see it in action:

var myApp = angular.module('myApp', []);

myApp.provider('myProvider', function() {
    this.phrase = "Hello World";
    this.$get = function() {
        var phrase = this.phrase;
        return {
            hello: function() { 
                return phrase; 
            }
        }
    };
});

myApp.config(function(myProviderProvider){
    myProviderProvider.phrase = 'Hello Yo!!';
});

But how is this possible? Well, at the moment of configuring our Angular application, we don't have any service instance available since the dependency injection has not happened yet. The magic happens because we have access to our provider class which is cached, and we can change properties that will be used later when they get initialized. To see this more clearly, let's check the source code:

function provider(name, provider_) {
    assertNotHasOwnProperty(name, 'service');
    if (isFunction(provider_) || isArray(provider_)) {
        provider_ = providerInjector.instantiate(provider_);
    }
    if (!provider_.$get) {
        throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
    }
    return providerCache[name + providerSuffix] = provider_;
}

Here we can see how our providers are cached on providerCache, which behaves similarly to a singleton pattern and, at the same time, we store the reference on providerInjector which do some checks, but doesn't invoke the $get method yet. Since our application knows we have a provider at configuration time, we will be able to change properties from the providerCache and, when the instances are created, our provider will use the properties changed.

Wrap it up

Probably you should use Services, unless you need to do something more complex. Anyhow, all the providers help you to abstract logic from the rest of your application and make them more reusable.

As a summary, don't forget that:

  • The difference between Service and Factory is the way they are instantiated.
  • Provider recipe is the base of all the providers, which are just syntactic sugar.
  • Providers are complex, but they let you create more flexible and reusable components since they are configurable.

If you want to play with this, I created a plunkr with code ready for you to try it, here.

I hope this can help you understand a bit more about how providers work.

Please ask away in the comments sections, or drop us a line if you have feedback.

Thanks for reading!

0 Shares:
You May Also Like