bstockwell
bstockwell

Reputation: 514

Factory patterns and ngResource

I have a factory that provides access to a resource object as shown below. Initially, I had this implemented inside a controller, but moved it up into a factory to make it more reusable across my whole app.

When I was building it, a line in the angular doc on providers caught my eye:

Best Practice: name the factory functions as Factory (e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase or looking at stack traces in the debugger. (source)

I'm obviously not doing that below (as I'm following a pattern from the resource Doc). But then I was trying to figure out what kind of provider this would qualify as and I'm not sure, and they don't include Best practice notes for any of the other kinds of providers detailed. Should I use service() instead of factory()? (I can't really find a clear answer.)

This question obviously has very low stakes, as the application works just fine (better than it did when I wasn't using the factory). But I'm curious about opinions.

...

.factory("Entries",Entries)

...

function Entries($resource,config) {
    endpoint = config.endpoint + ":" + config.port + "/entry";
    return $resource(endpoint + '/:id', {id:'@id'},
        { 'get':    {method:'GET'},
          'create': {method:'POST'},
          'update': {method:'PUT'},
          'query':  {method:'GET', isArray:true},
          'remove': {method:'DELETE'},
          'delete': {method:'DELETE'}
        }
    );
}

Upvotes: 1

Views: 440

Answers (1)

Dustin Hodges
Dustin Hodges

Reputation: 4193

If it works as a factory, keep it as a factory. I never use the service provider as I find that it doesn't provider anything over the factory method, and it is easier for my brain to grasp the factory pattern.

With a factory, you provide a function that returns the object you want angular's injector to use. With a service, you provide a Constructor function. The injector then creates the object that will be injected by newing up an object using the function provided. Angular factory's and service's are always singletons though so providing a Constructor function to new up a single object seems to me like a bit of overkill.

In your situation, if you converted it to a service, your object would basically just be acting as a proxy, something like below

...

.service("Entries",Entries)

...

function Entries($resource,config) {
    var endpoint = config.endpoint + ":" + config.port + "/entry";
    var resource = $resource(endpoint + '/:id', {id:'@id'},
        { 'get':    {method:'GET'},
          'create': {method:'POST'},
          'update': {method:'PUT'},
          'query':  {method:'GET', isArray:true},
          'remove': {method:'DELETE'},
          'delete': {method:'DELETE'}
        }
    );

    this.get = resource.get;
    this.create = resource.create;
    this.update = resource.update;
    this.query = resource.query;
    this.remove = resource.remove;
    this.delete = resource.delete;
}

That's pretty ugly and serves no purpose unless you really want to abstract away that you are using a $resource, which again you could do with a factory just as easily

...

.factory("Entries",Entries)

...

function Entries($resource,config) {
    var endpoint = config.endpoint + ":" + config.port + "/entry";
    var resource = $resource(endpoint + '/:id', {id:'@id'},
        { 'get':    {method:'GET'},
          'create': {method:'POST'},
          'update': {method:'PUT'},
          'query':  {method:'GET', isArray:true},
          'remove': {method:'DELETE'},
          'delete': {method:'DELETE'}
        }
    );

    return {
        get: resource.get,
        create: resource.create,
        update: resource.update,
        query: resource.query,
        remove: resource.remove,
        delete: resource.delete
    }
}

Even the angular documentation can't seem to provide a compelling reason for using a service. The example they give for when a service is most suitable is this (taken from here)

Given that you have an existing Constructor function

function UnicornLauncher(apiToken) {

  this.launchedCount = 0;
  this.launch = function() {
    // Make a request to the remote API and include the apiToken
    ...
    this.launchedCount++;
  }
}

a factory provider would look like

myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
  return new UnicornLauncher(apiToken);
}]);

while with a service you could make it a oneliner

myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);

Not too exciting to me and certainly not a good reason to make me go through the thought exercise of whether this should be a service or a factory. My advice is to forget that service() exists and just use factories

Upvotes: 1

Related Questions