AngularJS: $resource with nested resources
Asked Answered
B

4

12

I am using AngularJS $resource model to REST API. I have got something like this:

angular.module('libraryapp')
    .factory('Book', function($resource){
        return $resource('books/:id');
    });

I am using in these way:

Book.get({ id: 42 }, function(book) {
    console.log(book);
});

But I also want an endpoint to a subresource, let's say:

GET /books/:id/comments

How should I define it in module? May I extend Book in some way, to use it like this

Book.get({ id: 42 }).Comment.query(function(comments) {
    console.log(comments);
});
Backlash answered 14/11, 2014 at 10:52 Comment(0)
B
8

You can easily reach nested RESTful resources with AngularJS $resource definitions.

The clue is to understand how the params parameter of each action definition (in the list of actions) in the $resource definition works. As the documentation says, it's an

Optional set of pre-bound parameters for this action. […]

angular.module('libraryApp').factory('Book', [
  '$resource', function($resource) {
    return $resource('books/:id/:subResource', {}, {
      comments: {  // The `comments` action definition:
        params: {subResource: 'comments'},
        method: 'GET'
      }
    });
  }
]);

Given the above definition, you should still be able to use Book as before. For example Book.get({ id: 42 }) translates to a GET books/42/ request.

However, given the new :subResource part of the $resource URL ('books/:id/:subResource'), you now can generate a

GET books/42/comments

request by calling either Book.get({ id: 42, subResource: 'comments' }) or the much more short and elegant interface Book.comments({ id: 42 }) defined as your comments action.

Browne answered 7/12, 2015 at 0:6 Comment(3)
I know I answered this late, but if the answer works for you, it would be great if you accept it as the answer – so that other users can benefit from knowing that the answer works. If you had any issues, please let me know.Browne
The problem with this solution is that returned comments will be instances of Book resource. So if we call Book.comments({ id: 42 }).$promise.then(function (comments) { comments[0].$get(); }); then this $get will try to GET /books/<commend_id> and this is of course is not right.Bruni
djxak, Do you have an alternative to this problem?Task
A
2

As far as I know, you can't nest resources, but it's pretty simple to do what you're looking for:

You can define optional parameters which you can override in each resource (like category here) or even override the url (look at the otherUrl resource)

angular.module('libraryApp').factory('Book', [
  '$resource', function($resource) {
    return $resource('books/:id/:category', {}, {
      comments: {
        method: 'GET',
        action: 'category'
      },
      otherUrls: {
        method: 'GET',
        url: 'books/:id/admin/:option'
      }
    });
  }
]);
Aponeurosis answered 14/11, 2014 at 11:15 Comment(0)
A
2

You may want to use Restangular instead as it handles nested resources and a clean and easy way.

Asclepiadean answered 22/7, 2015 at 5:46 Comment(0)
A
0

As djxak pointed out, adding actions to the resource means that the returned value is the containing resource type, not the sub-resource type.

I solved a similar problem by creating a new resource with the sub-resource URL and modifying the prototype of the containing resource to add a function:

angular.module('libraryapp')
    .factory('Book', function($resource){
        var bookUrl = 'books/:id',
            Book = $resource(bookUrl),
            BookComment = $resource(bookUrl + /comments");

        Book.prototype.getComments = function () {
            return BookComment.query({id: this.id});
        };

        return $resource('books/:id');
    });

The usage then becomes:

Book.get({ id: 42 }).getComments(function(comments) {
    console.log(comments);
});

The only downside I see with this approach is that if you have a separate "Comment" resource that is accessed via a different URL, you have to duplicate the $resource initialisation code for the alternative endpoint. This seems a minor inconvenience though.

Araucania answered 20/6, 2018 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.