145
votes

I would like to use $resource to call my RESTful web service, (which I am still working on) but I would like to find out if I got my AngularJS script correct first.

The todo DTO has: {id, order, content, done}

:cmd is so I can call api/1/todo/reset to clear the todo table in database.

Here is the code with comment of my understanding:

function TodoService($resource) {
    var src = $resource('api/1/todo/:id:cmd',
              {id: "@id", cmd: "@cmd"}, //parameters default
              {
                ListTodos: { method: "GET", params: {} },
                GetTodo: { method: "GET", params: { id: 0 } },                            
                CreateTodo: { method: "POST", params: { content: "", order: 0, done: false } },
                UpdateTodo: { method: "PATCH", params: { /*...*/ } },
                DeleteTodo: { method: "DELETE", params: { id: 0 } },
                ResetTodos: { method: "GET", params: { cmd: "reset" } },
              });

    //Usage:

    //GET without ID
    //it calls -> api/1/todo
    src.ListTodos();

    //GET with ID
    //it calls -> api/1/todo/4
    src.GetTodo({ id: 4 });

    //POST with content, order, done
    //it calls -> api/1/todo
    src.CreateTodo({ content: "learn Javascript", order: 1, done: false });

    //UPDATE content only
    //it calls -> api/1/todo/5
    src.UpdateTodo({ id: 5, content: "learn AngularJS" }); 

    //UPDATE done only
    //it calls -> api/1/todo/5
    src.UpdateTodo({ id: 5, done: true });

    //RESET with cmd
    //it calls -> api/1/todo/reset
    src.ResetTodos();
}

One particular thing I am not sure is the PATCH method, I don't want to update everything, can I update just one field? Am I constructing this piece of code correctly?

2
It looks like you're using $resource as a basic $http service. $resource is more for getting an object from a RESTful data source, manipulating it, then sending it back with obj.save(). You could do what you're trying to do with a basic $http implementation.Ben Lesh
@blesh why shouldn't he use $resource when he wants to communicate with his RESTful webservice? As you said, isn't that exactly the purpose of it?F Lekschas
It looks for me but I would define the $resource as a service and inject it. This allows you to easily reuse it somewhere else later, if you need to.F Lekschas
@Flek Well, he can use $resource like $http if he wants to. But that's not really how it was meant to be used.Ben Lesh
Well, it's not really a "problem", per say. It's more that he's not taking any advantage of the RESTful api and all the stuff that $resource can do for you out of the box.Ben Lesh

2 Answers

212
votes

$resource was meant to retrieve data from an endpoint, manipulate it and send it back. You've got some of that in there, but you're not really leveraging it for what it was made to do.

It's fine to have custom methods on your resource, but you don't want to miss out on the cool features it comes with OOTB.

EDIT: I don't think I explained this well enough originally, but $resource does some funky stuff with returns. Todo.get() and Todo.query() both return the resource object, and pass it into the callback for when the get completes. It does some fancy stuff with promises behind the scenes that mean you can call $save() before the get() callback actually fires, and it will wait. It's probably best just to deal with your resource inside of a promise then() or the callback method.

Standard use

var Todo = $resource('/api/1/todo/:id');

//create a todo
var todo1 = new Todo();
todo1.foo = 'bar';
todo1.something = 123;
todo1.$save();

//get and update a todo
var todo2 = Todo.get({id: 123});
todo2.foo += '!';
todo2.$save();

//which is basically the same as...
Todo.get({id: 123}, function(todo) {
   todo.foo += '!';
   todo.$save();
});

//get a list of todos
Todo.query(function(todos) {
  //do something with todos
  angular.forEach(todos, function(todo) {
     todo.foo += ' something';
     todo.$save();
  });
});

//delete a todo
Todo.$delete({id: 123});

Likewise, in the case of what you posted in the OP, you could get a resource object and then call any of your custom functions on it (theoretically):

var something = src.GetTodo({id: 123});
something.foo = 'hi there';
something.UpdateTodo();

I'd experiment with the OOTB implementation before I went and invented my own however. And if you find you're not using any of the default features of $resource, you should probably just be using $http on it's own.

Update: Angular 1.2 and Promises

As of Angular 1.2, resources support promises. But they didn't change the rest of the behavior.

To leverage promises with $resource, you need to use the $promise property on the returned value.

Example using promises

var Todo = $resource('/api/1/todo/:id');

Todo.get({id: 123}).$promise.then(function(todo) {
   // success
   $scope.todos = todos;
}, function(errResponse) {
   // fail
});

Todo.query().$promise.then(function(todos) {
   // success
   $scope.todos = todos;
}, function(errResponse) {
   // fail
});

Just keep in mind that the $promise property is a property on the same values it was returning above. So you can get weird:

These are equivalent

var todo = Todo.get({id: 123}, function() {
   $scope.todo = todo;
});

Todo.get({id: 123}, function(todo) {
   $scope.todo = todo;
});

Todo.get({id: 123}).$promise.then(function(todo) {
   $scope.todo = todo;
});

var todo = Todo.get({id: 123});
todo.$promise.then(function() {
   $scope.todo = todo;
});
0
votes

you can just do $scope.todo = Todo.get({ id: 123 }). .get() and .query() on a Resource return an object immediately and fill it with the result of the promise later (to update your template). It's not a typical promise which is why you need to either use a callback or the $promise property if you have some special code you want executed after the call. But there is no need to assign it to your scope in a callback if you are only using it in the template.