Service vs Factory

Dave Ceddia bio photo By Dave Ceddia Comment

Should I use service or factory? What’s the difference?

Angular makes things a little confusing by giving you the option of registering objects as services or factories. Their behavior is very similar, and some people will say that either one is fine. That’s somewhat true, but I find it easier to follow the advice of John Papa’s style guide and just stick with factories.

But you came here to learn the difference between the two, so let’s look at that.

Both of them…

  • Are Singletons – No matter which you choose, they are both singletons. Angular will create a single instance the first time it sees a dependency on your service/factory, and use that single instance forevermore.

  • Can be used to model an object with behavior – They can both have methods, internal state variables, and so on. Though the way you write that code will differ, and we’ll see how that works below.

Factories

A factory is a plain old function that returns a value. The return value is what gets injected into things that depend on the factory. A typical factory pattern in Angular is to return an object with functions as properties, like this:

angular.factory('UserManager', function($http) {
  var api = {};

  api.getUsers = function() {
    return $http.get('/users');
  };

  api.deleteUser = function(id) {
    return $http.delete('/users/' + id);
  };

  return api;
});

Depending on your personal style, you might find it more readable to organize the code this way instead:

angular.factory('UserManager', function($http) {
  var api = {};

  api.getUsers = getUsers;
  api.deleteUser = deleteUser;

  return api;

  function getUsers() {
    return $http.get('/users');
  };

  function deleteUser(id) {
    return $http.delete('/users/' + id);
  };
});

It’s a little easier to tell at-a-glance what the factory exposes as its public API this way.

If it seems weird to you that there are functions defined after the return, perhaps you haven’t yet had your mind poisoned by become accustomed to JavaScript’s “hoisting” behavior. I won’t get into it here, but this is a good overview despite being a few years old.

As I mentioned above, the injected value for a factory dependency is the factory’s return value, and it doesn’t have to be an object. It could be a function (Angular’s built-in $filter does this). That would look something like this:

angular.factory('randomNumber', function() {
  return function() {
    return 42; // a suitably random number
  };
});

angular.controller('NumberDisplayCtrl', function($scope, randomNumber) {
  $scope.numberToDisplay = randomNumber();  // will get a random number (42)
});

Services

A service is a constructor function, and Angular will instantiate it by calling new yourService(). This means a couple things.

  1. Behavior (functions) and instance variables will be properties of this.
  2. You don’t need to return a value. When Angular calls new yourService(), it’ll receive the this object with all the properties you put on it.

An example service might look like this:

angular.service('UserManager', function($http) {
  this.getUsers = function() {
    return $http.get('/users');
  };

  this.deletehjsUser = function(id) {
    return $http.delete('/users/' + id);
  };
});

Notice: no return value, and methods are properties on this.

When Angular injects this UserManager service into a controller that depends on it, that controller will get a UserManager that it can call functions on, e.g. UserManager.deleteUser(7).

Be careful with this

Since the constructed service is an object, the methods inside it can refer to this when they’re called:

angular.service('ScoreKeeper', function($http) {
  this.score = 0;

  this.getScore = function() {
    return this.score;
  };

  this.setScore = function(newScore) {
    this.score = newScore;
  };

  this.addOne = function() {
    this.score++;
  };
});

You might be tempted to call ScoreKeeper.setScore in a promise chain, for instance if you initialized the score by grabbing it from the server: $http.get('/score').then(ScoreKeeper.setScore). The trouble with this is that ScoreKeeper.setScore will be called with this bound to null and you’ll get errors. The better way would be $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)), but it doesn’t read as nicely.

Whether you choose to use this in your service methods or not, be careful how you call them.

Returning a value from a service

Due to how JavaScript constructors work, if you return a complex value (i.e. an Object) from a constructor function, the caller will get that Object instead of the this instance.

This means that you can basically copy-paste the factory example from above, replace factory with service, and it’ll work:

// Call it a 'service'...
angular.service('UserManager', function($http) {
  var api = {};

  api.getUsers = function() {
    return $http.get('/users');
  };

  api.deleteUser = function(id) {
    return $http.delete('/users/' + id);
  };

  // Returning an object overrides 'this'
  return api;
});

So when Angular constructs your service with new UserManager(), it’ll get that api object instead of the UserManager instance.

This is the behavior for any complex values (objects, functions) but not for primitive types. Try it out! Open your browser console and type these in:

function c1() { return {a: 5}; }
function c2() { this.b = 6; }
function c3() { return function(x) { console.log('got', x); }; }
function c4() { return 42; }
function c5() { return Number(7); }

Then try executing each one as a regular function, and as a constructor function with new, like so:

c1()      // returns {a: 5}
new c1()  // returns {a: 5}

The best way to gain an intuitive understanding of this behavior is to play around with it.

Just Use Factories*

For the most part, just stick with using factories for everything. Their behavior is easier to understand. There’s no choice to make about whether to return a value or not, and furthermore, no bugs to be introduced if you do the wrong thing.

I still refer to them as “services” when I’m talking about injecting them as dependencies, though.

* You noticed that asterisk there?

Ok, well there are a few cases where a service is actually a better choice. As Mike mentioned in the comments, Angular’s built-in $resource service is a great example of something that makes more sense as a service, because it actually needs to be instantiated. It models data, in addition to having prototype methods for behavior.

So if you’re writing something that seems more like a proper “object oriented” object, consider doing it as a service instead of a factory.

Choosing a framework is difficult.

Learning React? Sign up and get my React Learning Timeline PDF, and also weekly-ish articles about React, Angular, and JavaScript.

A few emails per month — unsubscribe any time.
comments powered by Disqus