Angularjs $Resource Restful Example

AngularJS $resource RESTful example

$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;
});

Good example of a put operation using AngularJS $resource

If you're creating a new entity in your data store you want to use POST/save. If you're updating the data associated with an already existing entity in your data store you want to use PUT/update. Patch is usually reserved for when you just want to update a subset of the entity data.

Look at the RFC

Several applications extending the Hypertext Transfer Protocol (HTTP)
require a feature to do partial resource modification. The existing
HTTP PUT method only allows a complete replacement of a document. This
proposal adds a new HTTP method, PATCH, to modify an existing HTTP
resource.

You would supply an id with both PUT and PATCH operations. You would not supply one with a POST operation.

When we load our angular forms it is done one of two ways usually. If the form is loaded when we are creating a new entity then we won't have an id. We will know this in the controller and will call resource.save. If we supply the controller loading the form with an id that's used to pull data from an endpoint to populate the form, we now have the id we can use to do a resource.update or resource.patch operations depending on how much of the entity we are updating.

Here's an example save function that handles both update and save operations. Here we check to see if an id was supplied via the route before we make our resource call.

angular.module('appModule').controller('ExampleCtrl',
['$scope', '$routeParams',
function($scope, $routeParams) {

$scope.saveForm = function () {

//Do input validation before you make a resource call

if ($routeParams.id) {
//call resource update since we have an id
}
else {
//call resource save since we don't have an id
}
};
}]);

Here's the example from the angularjs documentation:

How to create a custom PUT request:

var app = angular.module('app', ['ngResource', 'ngRoute']);

// Some APIs expect a PUT request in the format URL/object/ID
// Here we are creating an 'update' method
app.factory('Notes', ['$resource', function($resource) {
return $resource('/notes/:id', null,
{
'update': { method:'PUT' }
});
}]);

// In our controller we get the ID from the URL using ngRoute and $routeParams
// We pass in $routeParams and our Notes factory along with $scope
app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
// First get a note object from the factory
var note = Notes.get({ id:$routeParams.id });
$id = note.id;

// Now call update passing in the ID first then the object you are updating
Notes.update({ id:$id }, note);

// This will PUT /notes/ID with the note object in the request payload
}]);

How to use an angularjs resource for rails/RESTful routes?

Have a look at restangular, its specifically designed to make HTTP verb requests to any REST api simple & easy.

https://github.com/mgonto/restangular

AngularJS $http and $resource

$http is for general purpose AJAX. In most cases this is what you'll be using. With $http you're going to be making GET, POST, DELETE type calls manually and processing the objects they return on your own.

$resource wraps $http for use in RESTful web API scenarios.


Speaking VERY generally: A RESTful web service will be a service with one endpoint for a data type that does different things with that data type based on HTTP methods like GET, POST, PUT, DELETE, etc. So with a $resource, you can call a GET to get the resource as a JavaScript object, then alter it and send it back with a POST, or even delete it with DELETE.

... if that makes sense.

AngularJS: Minimal example with $resource and timeout

The $resource factory was malformed:

/* ERRONEOUS
var myResource = $resource(url, {
method: "get",
timeout: 3000, // 1) timeout after 3s -> gets ignored
isArray: true,
cancellable: true
});
*/

//BETTER
var actions = {
timedGet: {
method: "get",
timeout: 3000
}
};
var myResource = $resource(url, {}, actions);

Action methods are defined as the third argument of the factory. The second argument is for default parameters.

Usage:

var x = myResource.timedGet();
x.$promise.then(function(response) {
$scope.response = response;
}, function(error) {
// Here I want to do sth. with the cancelled request
$scope.error = "Request took too long, cancelled. " + error;
});

The DEMO on JSFiddle

REST AngularJS @resource parametrized request

Based on OP's comment:

Say you want to update a single entity:

.controller('someCtrl', function($stateParams, eventHistoryFactory){
//For the sake of the demonstration - id comes from the state's params.
var eventHistory = eventHistoryFactory.get({id: $stateParams.id});

eventHistory.$promise.then(function(){
//Modify the entity when HTTP GET is complete
eventHistory.address = 'New York';

//Post the entity
eventHistory.$save();

//If you wish to use PUT instead of POST you should declare that
//in the class methods of $resource
});

//Another example using query
var entries = eventHistoryFactory.query({
page: 0,
size: 20,
before: Date.now()
});

//This is translated into GET /inner/service/eventhistories?page=0&size=20&before=111111111111
//and should be interpreted correctly by your backend.

entries.$promise.then(function(){
//entries now contain 20 first event history with date earlier than now.
var specificEntry = entries[0];

//Same deal - modify the entity when HTTP GET is complete
specificEntry.address = 'New York';

//Post the entity
specificEntry.$save();
});

AngularJS: Creating Objects that map to REST Resources (ORM-Style)

JSData
A project which started as angular-data is now "a framework-agnostic data store built for ease of use and peace of mind."
It is has excellent documentation and has support for relations, multiple backends (http, localStorage, firebase), validation and of course angular integration.

http://www.js-data.io/

BreezeJS
The AngularJS YouTube channel features this video using BreezeJS

Which is an advanced ORM which even supports client-side filtering and other cool stuff.
It best suited for backend that support OData, but can be made to work on other types of backends.

ngResource
Another option is to use the ngResource, here is an example on how to extend it with your own functions:

module.factory('Task', function ($resource) {
var Task = $resource(WEBROOT + 'api/tasks/:id', {id: '@id'}, {update: { method: 'PUT'}});
angular.extend(Task.prototype, {

anExampleMethod: function () {
return 4;
},

/**
* Backbone-style save() that inserts or updated the record based on the presence of an id.
*/
save: function (values) {
if (values) {
angular.extend(this, values);
}
if (this.id) {
return this.$update();
}
return this.$save();
}
});
return Task;
});

I found ngResource to be very limited, even compared to Backbone.Model which has:

  • Custom JSON parsing via Model.parse
  • Possible to extend a BaseModel (No the baseUrl in ngResource)
  • Other hooks like Backbone.sync, which enables LocalStorage, etc.

Restangular
"AngularJS service to handle Rest API Restful Resources properly and easily"

https://github.com/mgonto/restangular

Or try some of the other ORM's
https://stackoverflow.com/questions/6786307/which-javascript-orm-to-use



Related Topics



Leave a reply



Submit