Tuesday 13 May 2014

The magic of $resource

If you look at demos around using $resource in Angular then you'll see code something like this

app.factory("issuesResource", function ($resource) {
    return $resource('/api/issues/:id', {
        id: "@id"
    }, {
        update: {
            method: 'PUT'
        }
    });
});
so, what does this do/mean.

Breaking it down:

app.factory
creates a factory. Normally you would see something like this:
app.factory("myFactory", function(){
    return {
      doSomething: function(){}
    };
});
so when creating a $resource based service the code looks a little odd, it returns $resource(), so what does the call to $resource do?

$resource is "A factory which creates a resource object" according to the documentation, so what our code does is to take $resource (already a factory) and wrap it in our factory and then extend it. We extend it by telling it what the base URL is ('/api/issues' in this case), telling it where to get an 'id' value from when needed (more on this is a moment) and extending it with our own methods.

The last object passed to $resource

update: {
   method: 'PUT'
}
adds an 'update' method to $resource and causes $resource to use an HTTP PUT when this method is called.

The other parameter

{
    id: "@id"
}
is more interesting. According to the documentation then: "If the parameter value is prefixed with @ then the value of that parameter is extracted from the data object (useful for non-GET operations)." So this implies that the parameter is not used for GETs, and indeed this is so. I have a spec that looks like this:
it("should return an object when get is called", inject(function (issuesResource) {
    httpBackend.expectGET("/api/issues/1").respond(200, {id: 1});
    var issues = issuesResource.get({id: 1});
    httpBackend.flush();
    expect(issues.id).toBe(1);
}));
and this works whether the '@id' parameter is in place or not. For GETs it's the name of the placeholder in the URL that matters, so the 'id' in the '/api/issues/:id' has to match the 'id' in the call to
issuesResource.get({id: 1});
, however this is not true for POST, PUT or DELETE. In these cases the presence and name of the '@id' value is important.

So if we have a test for POST spec that looks like this:

it("should use the id passed in the object when posting", inject(function (issuesResource) {
    httpBackend.expectPOST("/api/issues/1").respond(200, {id: 1});
    var issues = issuesResource.save({id: 1});
    httpBackend.flush();
    expect(issues.id).toBe(1);
}));
then in the call to save the name of the property ('id') must match the name defined when creating the factory.

No comments:

Post a Comment