2

How can I use promise with $resource?

This is my service,

     app.service("friendService",function( $resource, $q ) {

        // Return public API.
        return({
            addFriend: addFriend,
            updateFriend: updateFriend,
            getFriends: getFriends,
            removeFriend: removeFriend
        });

        function updateFriend( friend ) {

            var postData = { 
                id: friend.id,
                name: friend.name
            };

            var request = $resource("api/update.php", null,{
                update: { 
                    method:'PUT',
                    data:postData
                }
            });


            return( request.$promise.then( handleSuccess, handleError ) );
        }

I get this error,

TypeError: request.$promise is undefined

What is the correct way doing it with $resource?

laukok
  • 47,545
  • 146
  • 388
  • 689

4 Answers4

3

Change from

return( request.$promise.then( handleSuccess, handleError ) );

to

return request.update().$promise.then(handleSuccess, handleError);

That said, using $resource like this is quite inefficient while not taking any advantage of it. It's better to replace with $http.

Buu Nguyen
  • 46,887
  • 5
  • 64
  • 84
  • tried that and got this - `Error: friendService.updateFriend(...).update is not a function` – laukok Nov 07 '14 at 19:23
  • I tried and didn't have that error. Here's the plunker: http://plnkr.co/edit/gKS1TdHaL3Bz1qOTekXg?p=preview. – Buu Nguyen Nov 07 '14 at 19:29
  • Got it fixed now thanks. I should use `.$promise.then(handleSuccess, handleError);` in the service but in the controller. – laukok Nov 07 '14 at 19:59
  • 1
    Glad it fixed the problem. If this answer pointed you to the right direction, please accept it. – Buu Nguyen Nov 07 '14 at 20:01
2

You should simplify your service to actually BE the $resource

app.factory('friendService', [ '$resource', function($resource) {
    return $resource('/api/friends/:id', null, {
        'update' : {
            method : 'PUT'
        }
    });
} ]);

This automatically provides the following endpoints (which actually is the cool thing about $resource):

{ 'get':    {method:'GET'},
  'save':   {method:'POST'},
  'query':  {method:'GET', isArray:true},
  'remove': {method:'DELETE'},
  'delete': {method:'DELETE'}
};

Here are some usage examples:

friendService.query(success, error); // GET /friends
friendService.get({ id : "exampleId" }, success, error); // GET /friends/exampleId
friendService.save({/* no params */}, friendObjectWithId, success, error); // POST /friends/idTakenFromObject
friendService.delete({ id : "exampleId" }, {}, success, error); // DELETE /friends/exampleId
friendService.update({/* no params */}, friendObjectWithId, success, error); // PUT /friends/idTakenFromObject

So, as this line of the documentation describes, you dont need the $promise to specify the callbacks:

non-GET "class" actions: Resource.action([parameters], postData, [success], [error])

So you can simply do something like this:

friendService.update({}, friendObject, successHandler, errorHandler)
Jonas
  • 1,275
  • 1
  • 17
  • 28
1

Short answer:

I think you are misunderstanding what $resource is, since you're trying to use it as you would use $http.

$resource is a "wrapper" around $http to provide a Object Oriented CRUD way to interact with a RESTful api. (DOCS explain it well and provide good examples)

From your URL, I don't think you're actually using a REST api so it would probably be better to use $http service instead of using $resource service.

Regardless, here's a working fiddle.


Resource and REST API

A resource, in the context of angular, corresponds to a resource in context of REST and so, it will expect your webservice to behave like a RESTful app. To explain it further, let' take your "Friend" as example... (I will be reworking your URLS to better match a REST API)

API Definition

Take the following REST+CRUD conformant scheme (for a Friend resource)

Resource            URI             Methods allowed
Friend Collection   api/friend      GET, POST
Friend              api/friend/:id  GET, PUT

The basic idea here is that each Resource is univocally represented by a URI (that's actually the definition of URI: -> Uniform Resource Identifier) and the HTTP Method (Verb) is used to define the action that will be performed on said Resource.

Of course, REST is much more than this and I suggest you read this SO POST or this funny article or even Roy Fielding's dissertation (the guy that came up with REST) that explain the concept a lot better than I ever can hope for.


URL Structure

This issue is prone to hot debate, and you can read some interesting points here in this SO Post and an article from Roy Fielding partially addressing this too. To sum up, REST does not require clean URLs. Actually, it doesn't require ANY kind of semantically logic URL structure.

What REST APIs must be is hypertext-driven, that is, given an entry point (URL), the API must be self explanatory so that a client can "discover" resources and relations by itself, with type of resources given by media-types. That means, if an url changes, the API doesn't break!!

So, in practical terms, this can be a valid:

Home                 /
Friend Collection    /foo
Friend Resource 1    /bar
Friend Resource 2    /baz

As well as this can be valid :

Home                index.php
Friend Collection   index.php?q=api/friend
Friend Resource 1   index.php?q=api/friend/1
Friend Resource 2   index.php?q=api/friend/2

Or it's cousin, using mod_reqrite to make "clean URLs", can be valid

Home                /
Friend Collection   /api/friend
Friend Resource 1   /api/friend/1
Friend Resource 2   /api/friend/1

or even this can be valid...

Home                /index.php
Friend Collection   /friend.php
Friend Resource 1   /friend_1.php
Friend Resource 2   /friend_2.php

The server, in no way, is obliged to follow any pattern. However, that doesn't mean you shouldn't adhere to a structure, must mostly for SEO purposes (or human readability). And, in the last example, it might be hard to develop a sane webservice that relies on individual scripts for each individual resource. (you might not violate REST principles, but you will probably violate some basic programming rules, such as DRY...)

Also, angular-resource is (kind of) opinionated about url structure. It's not an absolute requirement but...

Regarding your specific question, yes, you would need mod_rewrite to match the example I gave you. But you don't need mod_rewrite to make a REST compliant API.


Using angular-resource module

Now that our API scheme is set and follows the REST+CRUD principles, we can exploit the full potential of the angular-resource module.

We can create a client side representation(interface) of "Friend".

//Custom actions
var actions = {
    update: {
        method: 'PUT'
    }
}

var friendUrl = "/api/friend/:id"; // should be obtained by inspecting the API iteself, usually the parent collection. 
var Friend = $resource(friendUrl, {id: '@id'}, actions);

To get a friend we would issue a GET request (and specify it's id);

Friend.get({id: 1}).$promise.then(
    function (response) {
        //console.log(response);
    }
);

DELETE and PUT requests(which we created and called update) are basically the same thing. $resource also supports retrieving collections using the object's method query. You can use that to retrieve the collection of friends.

Please notice that I'm using a hardcoded URL for simplicity

Community
  • 1
  • 1
Tivie
  • 18,374
  • 5
  • 54
  • 77
  • Thanks. But what is the correct URL pattern for a a RESTful api then? – laukok Nov 07 '14 at 20:32
  • Thanks again for the update. But then don't we need to set mod_rewrite in a .htaccess for a URL like this `"/api/friend/:id"` otherwise how is it going to point to my php file to process the request?? – laukok Nov 08 '14 at 08:08
  • 1
    @tealou to match my example, yes, but preety URLs are not required by REST! – Tivie Nov 08 '14 at 16:29
  • `but pretty URLs are not required by REST` if it is not required, then how can we make REST work? How are we going to send data to our REST API **without** URL rewrite?? – laukok Nov 08 '14 at 17:57
  • @tealou What do you mean? You send data the usual way, in the request body. mod_rewrite is a apache rewrite engine to rewrite URLs on the fly. It's has nothing to do with how data is sent and processed – Tivie Nov 08 '14 at 22:47
  • if I send `/api/friend/1` I will get file not found in my browser console without rewriting the URL in a .htaccess. – laukok Nov 09 '14 at 14:54
  • 1
    @tealou Ah yes, of course the url must be valid. My point was this: **REST is not pretty URLs**. You don't need pretty urls like `/api/friend/1` to be RESTful. You can have, for instance, a file called friend.php inside a folder called api. And then use the url `example.com/api/friend.php?id=1`. – Tivie Nov 09 '14 at 15:46
  • 1
    then it all make sense now lol of course REST is not pretty URLs :D – laukok Nov 09 '14 at 16:26
  • 1
    @tealou Sorry. I misunderstood what you were asking and went a bit overboard with my explanation =P – Tivie Nov 09 '14 at 16:36
  • No worries Tivie. I appreciate your explanation and it sharpens my understanding on REST. I have been always confused by it. Thank you! ;-) – laukok Nov 09 '14 at 19:36
0

request is just setting up your endpoint. You need to actually call some method on it, e.g. request.get({id: 1}).$promise; or request.query({term: 'test'}).$promise;

Nat Wallbank
  • 1,277
  • 11
  • 12
  • if I am going to change to that `var request = $resource("api/update.php").save({}, postData).$promise.then( handleSuccess, handleError );` yes it works, but how can I change `.save()`'s method to `method:'PUT'`? by default, `.save()` sends `POST`. – laukok Nov 07 '14 at 19:27
  • Just call the custom .update() method you defined, which does a PUT. – Nat Wallbank Nov 07 '14 at 19:36
  • You can define the request first, as you did in your example (with the custom update method). Then just do request.update({ params }, data).$promise; – Nat Wallbank Nov 07 '14 at 19:41
  • tried that and still get this error - `Error: $resource(...).update is not a function` – laukok Nov 07 '14 at 19:44
  • Got it fixed now thanks. I should use `.$promise.then(handleSuccess, handleError);` in the service but in the controller. – laukok Nov 07 '14 at 19:59