145

What is the recommended way to connect to server data sources in AngularJS without using $resource.

The $resource has many limitations such as:

  1. Not using proper futures
  2. Not being flexible enough
Tushar
  • 85,780
  • 21
  • 159
  • 179
Misko Hevery
  • 47,936
  • 11
  • 40
  • 36
  • 6
    Is it still true that $resource has the limitation of not using proper futures given that this issue [(https://github.com/angular/angular.js/issues/415)](https://github.com/angular/angular.js/issues/415) has been closed? (I'm new to Angular, my apologies if the question is confusing.) EDIT - This commit (https://github.com/angular/angular.js/commit/dba6bc73e802fdae685a9f351d3e23c7efa8568a) also seems to suggest that $resource may not have that limitation anymore? – Jason Sydes Feb 28 '13 at 19:25
  • 1
    well it's legal in here to answer your own questions ..ok? press "Ask Question", that's called "share your knowledge" – holms Jul 20 '13 at 00:23

2 Answers2

230

There are cases when $resource may not be appropriate when talking to backend. This shows how to set up $resource like behavior without using resource.

angular.module('myApp').factory('Book', function($http) {
  // Book is a class which we can use for retrieving and 
  // updating data on the server
  var Book = function(data) {
    angular.extend(this, data);
  }

  // a static method to retrieve Book by ID
  Book.get = function(id) {
    return $http.get('/Book/' + id).then(function(response) {
      return new Book(response.data);
    });
  };

  // an instance method to create a new Book
  Book.prototype.create = function() {
    var book = this;
    return $http.post('/Book/', book).then(function(response) {
      book.id = response.data.id;
      return book;
    });
  }

  return Book;
});

Then inside your controller you can:

var AppController = function(Book) {
  // to create a Book
  var book = new Book();
  book.name = 'AngularJS in nutshell';
  book.create();

  // to retrieve a book
  var bookPromise = Book.get(123);
  bookPromise.then(function(b) {
    book = b;
  });
};
Community
  • 1
  • 1
Misko Hevery
  • 47,936
  • 11
  • 40
  • 36
  • 1
    Maybe a noob question but how would one extend this to allow for a get() that returned multiple responses not just a single response? – Nate Bunney Aug 13 '12 at 21:14
  • 2
    @NathanBunney, you could have your static get method above loop through the results and build an array of Books. – Ben Lesh Dec 13 '12 at 20:28
  • 4
    @NathanBunney ' Book.getAll = function() { return $http.get('/Book').then(function(response) { var books = []; for(var i = 0; i < response.data.length; i++){books.push(new Book(response.data[i]));} return books; }); };' – Yair Nevet Jan 26 '13 at 23:11
  • why do i get `Unknown provider: BookProvider <- Book` Error ?? – Amyth Feb 25 '13 at 12:19
  • 2
    How would you share a `book` or collection of `book`s between controllers? – Lukas Apr 17 '13 at 03:51
  • @MiskoHevery Why does Book.get() call to the server only once and then when called again from the controller - it doesn't poll the server any more? I want to use it in the $timeout and have that issue... – alchemication Jun 07 '13 at 16:13
  • In the controller I had to remove `new Book()` and just make it `var book = Book`. I'm not well versed enough in JS to say why. – Dcritelli Aug 29 '13 at 19:51
  • can anybody explain me what is the purpose of return new Book(response.data); instead of return response.data; – harikrish Apr 16 '14 at 12:01
  • You seem to assume that your $http.get cannot fail? – Mawg says reinstate Monica May 14 '14 at 05:09
  • @harikrish The idea is to return an object that has methods you can call on it like "update"; which wasn't shown, I assume for brevity. You can see how "update" can be implemented by looking at "create". – event_jr May 22 '14 at 12:50
  • 1
    This is great (obviously since Misko created the framework), but I'm struggling to figure out how to make it reusable. If I wanted to implement the CRUD functions only once and then inherit their functionality in child factories/services that needed only a urlBase argument (preferably stored on the prototype so that each instance didn't need a new copy of the urlBase), how would I do that? – Dean Stamler Jul 21 '14 at 15:55
26

I recommend that you use $resource.

It may support (url override) in next version of Angularjs. Then you will be able to code like this:

// need to register as a serviceName
$resource('/user/:userId', {userId:'@id'}, {
    'customActionName':    {
        url:'/user/someURI'
        method:'GET',
        params: {
            param1: '....',
            param2: '....',
        }
    },
     ....
});

And return callbacks can be handled in ctrl scope like this.

// ctrl scope
serviceName.customActionName ({
    paramName:'param',
    ...
}, 
function (resp) {
    //handle return callback
}, 
function (error) {
    //handler error callback
});

Probably you can handle code on higher abstraction level.

ntalbs
  • 28,700
  • 8
  • 66
  • 83
thanksnote
  • 1,012
  • 11
  • 9