When To Use a Service?

Services are singletons. That is to say, you only get one, ever. Every time you inject a service into your controller or wherever, you get the same object. In my experience, I try to use a service first, then consider the other two. The reason being is that a service simply takes up less memory and processing power than the others. It is also probably the most dead-simple to write since you can stuff everything into the service’s this and don’t have to return anything. Think of the service as a constructor function that gets called only once, and the resulting object can be used just about anywhere.

When To Use a Factory?

When what you want to provide is not an object–perhaps it’s just a function, or a class definition–use a factory. If you want to have new objects each time they are injected, you would not use a service. Say you had a filter-like function that you wanted to provide to your controllers, but you know you’d never use it in a view, and you don’t want to murk up some namespace by declaring it outside of angularjs‘ context. Factories to the rescue!

 var someFunction = function() { ... }; 
// becomes... 

some_module.factory('someFunction', function() { // injectable too!
    return function() { ... };
});

I find this pattern to be extremely handy, and it keeps my namespaces pristine. Don’t make that global function!!! Put it in a factory. Here’s a simple example of creating and using a class, which we’ll use in our blog. This class will describe a blog post; a Post object. We’ll need to define our factory, then update our controller to use it:

 // returns a class Post which represents a blog post
blog.factory('Post', function () { 

    // set date, author, and temporary title and body
    // date can be a Date object or a JS timestamp or anything else Date objects accept
    var Post = function (title, body, date, author, autosave) {
        this.date = new Date(date);
        this.author = author;
        if (autosave) {
            this.title = title;
            this.body = body;
            this.temp = {};
       } else {
            this.temp = {title: title, body: body};
       }
    }; 

    // update the date to NOW
    Post.prototype.updateDate = function () {
        this.date = new Date(); // js Date object
    };  

    // save the temp info to the real info, then update the date to now.
    Post.prototype.save = function () {
        this.title = this.temp.title;
        this.body = this.temp.body;
        this.updateDate();
        this.stopEditing();
    };

    // prepares this post for editing
    Post.prototype.beginEditing = function () {
        this.editing = true;
        this.temp.title = this.title;
        this.temp.body = this.body;
    };

    // takes the post out of 'editing' mode
    Post.prototype.stopEditing = function () {
        this.editing = false;
        this.temp = {};
    };

    // handiness to convert this Post to a string, for debugging purposes
    Post.prototype.toString = function() {
        return angular.toJson(this);
    }; 
    return Post;
});