Angular.js

Understanding Providers, Services, and Factories in Angular

I’ve read quite a few posts (as well as questions posed) about the differences between services and factories in Angular. I’ve seen just as many attempts to explain it and even fell victim to trying to force an explanation for the difference myself. The answer is more simple than you may think and is evident if you browse the Angular source code and then re-read the documentation. When this clicked for me I had to share it because so many people seem to make more out of it than it is.

The biggest misconception I see is the idea that factories and services have anything to do with being a singleton. The reality is that both methods create singletons (unless you game them to generate new instances).

So here it is. I’m going to summarize then follow up with some examples;

  • Use a service when you want to pass a constructor function. The function will be invoked with the “new” operator and the result stored.
  • Use a factory when you want to return the object from a function (i.e. factory pattern). The function will be called and the result stored.
  • Use a provider when you want to be able to provide module-wide configuration for your object before making it available.

That’s it! It’s real simple. The key to note first is that factories and services end up with the same result, they just use different approaches. With the factory approach you can specify a function to resolve your dependencies and then return an object that uses them, like this:

app.factory('myAlertFactory', ['myAlert', function (myAlert) {
    return {
        alert: function (message) {
            myAlert.alert(message);
        }
    };
}]); 

Notice that the outer function takes a dependency on a service called “myAlert”. Angular will wire that dependency, call your function and store the result. In this case I pass back an object with an alert method that “passes through” to the alert method on the myAlert service. Essentially I’ve created a “myAlertFactory” object I can use to reference that is a proxy to the underlying “myAlert” object.

The problem with the factory approach is that some languages like TypeScript lean you towards a more class-based approach, and other languages like CoffeeScript enforce it. You can’t just return an arbitrary object from the factory function, so instead you need a way to pass the type onto Angular (remember in JavaScript a custom type is really a constructor function).

This will work fine:

app.service('myAlertService', MyAlertService); 

The type is defined like this:

function MyAlertService(myAlert) {
    this.alert = function (message) {
        myAlert.alert(message);
    };
}
MyAlertService.$inject = ['myAlert'];
 

Notice that this is a constructor function. It also takes on a dependency, only this time I used a different method to annotate the class so Angular knows what to inject. The end result is exactly the same, so your choice of factory or service should be based on your preference for supplying the instance – do you prefer to create classes and pass the constructor function, or would you rather return something explicitly from a factory function? It’s your choice. The service and factory calls wire up the object and refer to how you prefer to make it, not how the app will consume it.

So what about provider? A provider is a special case that allows for configuration. If you don’t need module-wide configuration, go for a service or factory instead. Think of a provider as sitting on top of a service. The provider is an object that can handle configuration. The first time the service itself is needed, Angular will call the $get function on the provider. This will return an instance that is subsequently tracked like any other item that was wired up using a service or factory.

To illustrate, let’s implement the “myAlert” service that was passed into the two previous examples. The service does what you’d expect – it raises an alert – but it can be configured one time only to prepend a date. It will either use the message, or use the combination of the current date and the message. The provider looks like this:

function MyAlertProvider() {
    var showTime = false;
    this.setShowTime = function (show) {
        showTime = !! show;
    };
    this.$get = ['$window',
    function ($window) {
        return {
            alert: function (message) {
                var dateStamp = new Date();
                if (showTime) {
                    $window.alert(dateStamp.toString() + ": " + message);
                } else {
                    $window.alert(message);
                }
            }
        };
    }];
}
 

It contains an internal variable for configuration, exposes a method to configure it, and supplies a $get method to generate the instance (in this case, we’re using the inline annotation to inject the $window service). Note the convention is to take the name you’re going to use for the service and append “Provider” to the end. Here is the set up for the provider. Notice it is named without the provider suffix:

var app = angular.module('myApp', []);
app.provider('myAlert', MyAlertProvider); 

And here the module configures it to prepend the date. Notice the dependency requested is for the provider, not the service itself:

app.config(['myAlertProvider', function (myAlertProvider) {
    myAlertProvider.setShowTime(true);
}]); 

Finally, I can take all of these items and expose them in a controller like this:

app.controller('MyController', [
    'myAlert',
    'myAlertService',
    'myAlertFactory',
    '$scope', function (myAlert, myAlertService, myAlertFactory, $scope) {
    $scope.alert = function () {
        myAlert.alert("This is an alert!");
    };
    $scope.alertService = function () {
        myAlertService.alert("This is an alert!");
    };
    $scope.alertFactory = function () {
        myAlertFactory.alert("This is an alert!");
    };    
}]); 

That’s it. The behavior of the alert calls depends on the one-time configuration of the provider. The service and factory proxies are identical despite the different way they were wired up. If I wanted to make the optional date a parameter and not a configuration option, I would do away with the provider and just use a service or a factory. The full working example is available to you at the following link. Try commenting out the .config call to see the default alert without dates added.
http://jsfiddle.net/jeremylikness/A6Cb2/

Jeremy Likness

Jeremy Likness is a principal architect at iVision, Inc. He has been building enterprise applications using the Microsoft stack for 20 years with a focus on web-based solutions for the past 15. A prolific author and speaker, Jeremy's mission is to empower developers to create success in their careers through learning and growth.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button