Search code examples
angularjsangular-resource

Extract data from $resource.get(...).$promise


I use $resource to get data from Facebook's Graph api:

resource = $resource(
    "https://graph.facebook.com/v2.2/:api/:id/:node/"
)

So for instance, I made a successful request to 'https://graph.facebook.com/v2.2/user.id/albums/' by:

resource.get({id:user_id, node: "albums", access_token: ....}).then(function(response)
{
    console.log(response)
})

and the response shows (in Chrome dev tool console):

Resource
- $promise: Promise
- $resolved: true
- data: Array[9] 
    - 0: Object
    - 1: Object2:
    - length: 9
    - __proto__: Array[0]
- paging: Object
- __proto__: Resource

So I naively try to add under console.log response another console.log response.data,

but it shows 'undefined'.

So I wonder how to extract the data object?

========================== EDIT ========================

It seems the reason being

resource.get({id:user_id, node: "albums", access_token: ....}).then(function(response)
{
    console.log(response)
})

is chained after another resource request as follows:

    FB.get_user().then(function(response) {
  var user_id;
  return user_id = response.id;
}).then(function(response) {
  return self.albums = FB.resource.get({
    id: user_id,
    node: "albums",
    access_token: Auth.get_user().social_account_access_token
  });
}).then(function(response) {
  console.log("response", response); # Log #1
  return console.log("response.data", response.data); # Log #2
});

In this case Log #1 will log out the resource object with data being an array, while Log #2 gives undefined.

If I do not chain the then function, but put the last one inside the previous .then, I have the expected results:

FB.get_user().then(function(response) {
  var user_id;
  return user_id = response.id;
}).then(function(response) {
  return self.albums = FB.resource.get({
    id: user_id,
    node: "albums",
    access_token: Auth.get_user().social_account_access_token
  }).$promise.then(function(response) {
    console.log("A: response", response);  # Log #1
    return console.log("response.data", response.data); # Log #2
  });
});

gives Log #1 the same result, while Log #2 is an array of 9 elements.

** So I wonder I is the problem of the original method?**



Solution

  • What's happening in your first attempt is that in your second then() you are returning the return value of FB.resource.get(), but this value is not a promise, so this then() resolves immediately, and processing moves on to the next then() before the data is done being retrieved. When you view the values in your Chrome debugger, you are stopping the execution long enough for the request to finish, and the data is populated when you observe it. (There is a term for this phenomenon, by the way.)

    According to the notes on this pull request and this note in the developer's guide, you should be using instance.$promise if you want to chain off of a resource request. So your second approach that uses $promise is more or less the correct way to go about it.

    Your code can be cleaned up a bit. Unless you have a reason to want a separate step to extract the FB user id and pass it to the next step, you can remove the first .then(). There is some other tidying up that you can do as well:

    FB.get_user()
    .then(function (response) {
        var user_id = response.id;
        // the value assigned to self.albums here is a (mostly) empty object that will 
        // be populated with data when the request finishes
        self.albums = FB.resource.get({
            id: user_id,
            node: "albums",
            access_token: Auth.get_user().social_account_access_token
        });
        return self.albums.$promise;
    })
    .then(function (response) {
        // (avoid putting a [then] inside another [then] unless you need to)
        console.log("A: response", response);
        console.log("response.data", response.data);
        return response.data;
    });