Not the easiest issue to put into a title.
Anyhow, my app is built on nodejs/expressjsand has an API set up for the url:
EDIT: The current code I'm using is:
$scope.updateProduct = $resource('/api/updateProduct/:product/:param/:value',{},{
query: {method:'GET'},
post: {method:'POST'},
save: {method:'PUT', params: {brand: '#brand', param:'#param', value:'#value'}},
remove: {method:'DELETE'}
});
$scope.updateProduct.save({
product : $scope.post._id,
param: 'likes',
value: $scope.user._id
});
At present it calls /api/updateProduct instead of /api/updateProduct/<product>/<param>/<value> like it's supposed to / like it does when I perform $scope.updateProduct.get().
In my console I see (as an example):
PUT /api/updateBrand/Quay%20Eyewear%20Australia/userTags/sunglasses,%20classic 200 30ms - 2.31kb
However, the API isn't actually accessed/nothing happens. Interestingly, if I go to localhost:5000/api/updateBrand/Quay%20Eyewear%20Australia/userTags/sunglasses,%20classic in my browser, it posts the correct data and updates the product in my database, so it's definitely an error with the way the $resource is being called.
As you can see in ngResource docs, $resource receive 3 parameters:
$resource(url[, paramDefaults][, actions]);
You are passing your action as a parameter.
The correct version would be:
$scope.updateProduct = $resource('/api/updateProduct/:product/:param/:value',{}, {'save':{method:'POST'}});
Note that it isn't even necessary, because when you use $resource you already create the default methods:
{
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
First of all, your have defined the save() to have a parameter called "brand", but in your url template and in the call to save(), you are using "product". I guess it's a typo.
You say when you are using browser to visit the url, it works well; but when angular is make a PUT request to the same url, nothing is happening. This indicates that your backend is configure to process only GET requests for this particular url pattern. Therefore, you need to make sure that your backend is accepting PUT requests.
I was struggling with this issue and was able to pass parameters to the resource by doing the equivalent call below (notice the '$' before 'save').
$scope.updateProduct.$save({
product : $scope.post._id,
param: 'likes',
value: $scope.user._id
});
I also did not define a 'save' method in the resource. According to Angular docs:
"Calling these methods (meaning non-GET methods) invoke $http on the url template with the given method, params and headers. When the data is returned from the server then the object is an instance of the resource type and all of the non-GET methods are available with $ prefix. This allows you to easily support CRUD operations (create, read, update, delete) on server-side data."
Related
How can I use promise with $resource?
This is my service,
app.service("friendService",function( $resource, $q ) {
// Return public API.
return({
addFriend: addFriend,
updateFriend: updateFriend,
getFriends: getFriends,
removeFriend: removeFriend
});
function updateFriend( friend ) {
var postData = {
id: friend.id,
name: friend.name
};
var request = $resource("api/update.php", null,{
update: {
method:'PUT',
data:postData
}
});
return( request.$promise.then( handleSuccess, handleError ) );
}
I get this error,
TypeError: request.$promise is undefined
What is the correct way doing it with $resource?
Change from
return( request.$promise.then( handleSuccess, handleError ) );
to
return request.update().$promise.then(handleSuccess, handleError);
That said, using $resource like this is quite inefficient while not taking any advantage of it. It's better to replace with $http.
You should simplify your service to actually BE the $resource
app.factory('friendService', [ '$resource', function($resource) {
return $resource('/api/friends/:id', null, {
'update' : {
method : 'PUT'
}
});
} ]);
This automatically provides the following endpoints (which actually is the cool thing about $resource):
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
Here are some usage examples:
friendService.query(success, error); // GET /friends
friendService.get({ id : "exampleId" }, success, error); // GET /friends/exampleId
friendService.save({/* no params */}, friendObjectWithId, success, error); // POST /friends/idTakenFromObject
friendService.delete({ id : "exampleId" }, {}, success, error); // DELETE /friends/exampleId
friendService.update({/* no params */}, friendObjectWithId, success, error); // PUT /friends/idTakenFromObject
So, as this line of the documentation describes, you dont need the $promise to specify the callbacks:
non-GET "class" actions: Resource.action([parameters], postData, [success], [error])
So you can simply do something like this:
friendService.update({}, friendObject, successHandler, errorHandler)
Short answer:
I think you are misunderstanding what $resource is, since you're trying to use it as you would use $http.
$resource is a "wrapper" around $http to provide a Object Oriented CRUD way to interact with a RESTful api. (DOCS explain it well and provide good examples)
From your URL, I don't think you're actually using a REST api so it would probably be better to use $http service instead of using $resource service.
Regardless, here's a working fiddle.
Resource and REST API
A resource, in the context of angular, corresponds to a resource in context of REST and so, it will expect your webservice to behave like a RESTful app. To explain it further, let' take your "Friend" as example... (I will be reworking your URLS to better match a REST API)
API Definition
Take the following REST+CRUD conformant scheme (for a Friend resource)
Resource URI Methods allowed
Friend Collection api/friend GET, POST
Friend api/friend/:id GET, PUT
The basic idea here is that each Resource is univocally represented by a URI (that's actually the definition of URI: -> Uniform Resource Identifier) and the HTTP Method (Verb) is used to define the action that will be performed on said Resource.
Of course, REST is much more than this and I suggest you read this SO POST or this funny article or even Roy Fielding's dissertation (the guy that came up with REST) that explain the concept a lot better than I ever can hope for.
URL Structure
This issue is prone to hot debate, and you can read some interesting points here in this SO Post and an article from Roy Fielding partially addressing this too. To sum up, REST does not require clean URLs. Actually, it doesn't require ANY kind of semantically logic URL structure.
What REST APIs must be is hypertext-driven, that is, given an entry point (URL), the API must be self explanatory so that a client can "discover" resources and relations by itself, with type of resources given by media-types. That means, if an url changes, the API doesn't break!!
So, in practical terms, this can be a valid:
Home /
Friend Collection /foo
Friend Resource 1 /bar
Friend Resource 2 /baz
As well as this can be valid :
Home index.php
Friend Collection index.php?q=api/friend
Friend Resource 1 index.php?q=api/friend/1
Friend Resource 2 index.php?q=api/friend/2
Or it's cousin, using mod_reqrite to make "clean URLs", can be valid
Home /
Friend Collection /api/friend
Friend Resource 1 /api/friend/1
Friend Resource 2 /api/friend/1
or even this can be valid...
Home /index.php
Friend Collection /friend.php
Friend Resource 1 /friend_1.php
Friend Resource 2 /friend_2.php
The server, in no way, is obliged to follow any pattern. However, that doesn't mean you shouldn't adhere to a structure, must mostly for SEO purposes (or human readability). And, in the last example, it might be hard to develop a sane webservice that relies on individual scripts for each individual resource. (you might not violate REST principles, but you will probably violate some basic programming rules, such as DRY...)
Also, angular-resource is (kind of) opinionated about url structure. It's not an absolute requirement but...
Regarding your specific question, yes, you would need mod_rewrite to match the example I gave you. But you don't need mod_rewrite to make a REST compliant API.
Using angular-resource module
Now that our API scheme is set and follows the REST+CRUD principles, we can exploit the full potential of the angular-resource module.
We can create a client side representation(interface) of "Friend".
//Custom actions
var actions = {
update: {
method: 'PUT'
}
}
var friendUrl = "/api/friend/:id"; // should be obtained by inspecting the API iteself, usually the parent collection.
var Friend = $resource(friendUrl, {id: '#id'}, actions);
To get a friend we would issue a GET request (and specify it's id);
Friend.get({id: 1}).$promise.then(
function (response) {
//console.log(response);
}
);
DELETE and PUT requests(which we created and called update) are basically the same thing. $resource also supports retrieving collections using the object's method query. You can use that to retrieve the collection of friends.
Please notice that I'm using a hardcoded URL for simplicity
request is just setting up your endpoint. You need to actually call some method on it, e.g. request.get({id: 1}).$promise; or request.query({term: 'test'}).$promise;
I have a JS/HTML5 Project based on angularjs where I protect the api with an authorization token set in the http header. Now I also want to protect the access to images from the server.
I know how to do it on the server side, but how can I add HTTP Headers to image requests in angular or javascript? For api request we have already added it to the services ($ressource) and it works.
In Angular 1.2.X
There are more than a few ways to do this. In Angular 1.2, I recommend using an http interceptor to "scrub" outgoing requests and add headers.
// An interceptor is just a service.
app.factory('myInterceptor', function($q) {
return {
// intercept the requests on the way out.
request: function(config) {
var myDomain = "http://whatever.com";
// (optional) if the request is heading to your target domain,
// THEN add your header, otherwise leave it alone.
if(config.url.indexOf(myDomain) !== -1) {
// add the Authorization header (or custom header) here
config.headers.Authorization = "Token 12309123019238";
}
return config;
}
}
});
app.config(function($httpProvider) {
// wire up the interceptor by name in configuration
$httpProvider.interceptors.push('myInterceptor');
});
In Angular 1.0.X
If you're using Angular 1.0.X, you'll need to set the headers more globally in the common headers... $http.defaults.headers.common.Authentication
EDIT: For things coming from
For this you'll need to create a directive, and it's probably going to get weird.
You'll need to:
Create a directive that is either on your <img/> tag, or creates it.
Have that directive use $http service to request the image (thus leveraging the above http interceptor). For this you're going to have to examine the extension and set the proper content-type header, something like: $http({ url: 'images/foo.jpg', headers: { 'content-type': 'image/jpeg' }).then(...)
When you get the response, you'll have to take the raw base64 data and set the src attribute of your image element to a data src like so: <img src="data:image/jpeg;base64,9hsjadf9ha9s8dfh...asdfasfd"/>.
... so that'll get crazy.
If you can make it so your server doesn't secure the images you're better off.
As said here you can use angular-img-http-src (bower install --save angular-img-http-src if you use Bower).
If you look at the code, it uses URL.createObjectURL and URL.revokeObjectURL which are still draft on 19 April 2016. So look if your browser supports it.
In order to use it, declare 'angular.img' as a dependency to your app module (angular.module('myapp', [..., 'angular.img'])), and then in your HTML you can use http-src attribute for <img> tag.
For example: <img http-src="{{myDynamicImageUrl}}">
Of course, this implies that you have declared an interceptor using $httpProvider.interceptors.push to add your custom header or that you've set statically your header for every requests using $http.defaults.headers.common.MyHeader = 'some value';
we have the same issue and solve it using a custom ng-src directive ..
basically a secure-src directive which does exactly what ng-src does (its a copy basically) BUT it extends the url with a query parameter which includes the local authentication header.
The server code returning the resources are updated to not only check the header but also the query parameters. of course the token added to the query parameter might be authenticated slightly differently.. e.g. there might be a time window after after a normal rest request in which such a request is allowed etc .. since the url will remain in the browser history.
From oficial documentation at: http://docs.angularjs.org/api/ngResource/service/$resource
Usage
$resource(url, [paramDefaults], [actions]);
[...]
actions: Hash with declaration of custom action that should extend the
default set of resource actions. The declaration should be created in
the format of $http.config:
{action1: {method:?, params:?, isArray:?, headers:?, ...},
action2: {method:?, params:?, isArray:?, headers:?, ...}, ...}
More about $http service:
http://docs.angularjs.org/api/ng/service/$http#usage_parameters
As pointed out Here FIrefox supports httpChannel.setRequestHeader :
// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);
In the example code above we have a variable named httpChannel which
points to an object implementing nsIHttpChannel. (This variable could
have been named anything though.)
The setRequestHeader method takes 3 parameters. The first parameter is
the name of the HTTP request header. The second parameter is the value
of the HTTP request header. And for now, just ignore the third
parameter, and just always make it false.
However this seems to be only available on Firefox (link)
You can either use Cookies to pass the value and retrieve it as a cookie instead of a HttpHeader or use ajax to retrieve the image with a custom header.
More links :
link1
link2
My app is Backbone.js for client-side, Express.js for back-end.
I have problems with syncing with all parts of my API, using the backbone model and collection(they use urlRoot: "/users").
I'm allowed to use only GET or POST, no PUT or DELETE.
*I'm not allowed to use more models*
Not allowed to use jQuery ajax
My API
add new user:
I need to make a POST to /users with JSON of new data. So I did it just fine with - this.model.save({new data...})
list all users:
My API for that, responses to GET /users, with the right handler - so, this.collection.fetch() - works fine.
Log-In:
My API accepts POST to /users/login for that. How can I add a function "logIn" to my model, that will use custom sync/pass options.url to sync - or any other way - that will POST to /users/login ?
Log-Out:
API accepts POST to /users/logout - how to send this request using my backbone model ?
User By ID:
Same question here for GET /users/:id
Update user:
POST /users/:id - same question again.
--- So actually, the question in short is ---
What is the best way (or the most "right"), to implement methods of a backbone model, that are similar to "model.save()" - but just POST/GET to a bit different path then urlRoot ?
You probably have a couple options here. One would be structuring your models in a way that supports the urls you want. For instance, have a User model and a Session model that deal with updating the user and managing the logged in state separately.
The other thing you should probably do is to use the url method in your models.
Something like this in your User model. (Note: using urlRoot instead of url here is identical, but this is the correct approach for anything more complicated that is needed in the url)
url : function() {
var base = "/users/";
if(this.isNew()) {
return base;
} else {
return base + this.get("id");
}
}
You could extend this same concept to work in your Session model for handling logout vs login based on if the Session is new or not.
Update:
If you must use the same model, the best thing would be to totally bypass the Backbone.sync method and write a custom AJAX call with success/error handlers that know how to clean things up.
login : function() {
$.post("/users/login", {success: function (response) {
// Update user as needed
}, error: function (xhr, response) {
// Handle errors
}
}
}
I don't quite get how to properly use AngularJS's $resource. For example, I have a REST API that is returning data like this:
{
"status": "success",
"data": {
"expand": "schema,names",
"startAt": 0,
"maxResults": 10,
"total": 38,
"issues": [{ ... }, { ... }, {...}, ...]
}
}
Now what I am trying to figure out is how in AngularJS to use the $resource where each object in data.issues is returned as a resource (so in this case, get an array/collection of 10 resources) however it does not really seems like I can do that with AngularJS's $resource from the limited resource I have found on it, or can I?
$resource is expecting a classic "RESTful" API source. This means you'd have an endpoint where GET, PUT, POST, DELETE methods would all effect a given resource type, and the body of those messages would only include the resource, not the resource and a bunch of metadata.
For what you're trying to do, if that's the API you're stuck with, you're probably going to have to use $http to roll your own, as it looks like the JSON it's responding with contains a bunch of metadata that $resource won't care about.
The only other option would be to write some sort of httpInterceptor that would translate what you're getting back from your web service as into something $resource can handle a little more seamlessly.
While, semantically, your web service is probably generically "RESTful", it's not RESTful in the current classic standard of what that means.
More information on REST here
EDIT: Outside of the above information, without seeing the signature of your web API or knowing what you're trying to do, it will be hard to answer your question in any more detail.
With newer versions of angular, you can provide a callback to a resource action that will bypass an outer wrapper.
$resource('', {}, {
query: {
method: 'GET',
isArray: true,
transformResponse: function(response) {
return JSON.parse(response).data.issues;
}
}
});
Then in your resource callback function
Issue.query({
//query params
}, function(issues) {
//issues will be an array of your issues that you can process
//or just add to the scope to be iterated over
});
Looking at the code, blesh is correct so what I did was modify the base $resource code in order to support custom encoders/decoders to make is any to have $resource be able to work with any REST API that might have a customized format. Made a pull request to angularjs hopping they will include it so I don't have to maintian this separate fork : https://github.com/angular/angular.js/pull/1514
Say I have the following ember-data model:
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
starred: DS.attr('boolean')
});
This communicates with a Rails app with the following pretty standard CRUD API:
GET /people - get a list of people
POST /people - create a new person
GET /people/id - get a specific person
PUT /people/id - update a specific person
DELETE /people/id - delete a specific person
This all maps to Ember-Data with the standard Store/Adapter.
However, lets say that in order to "star" or "unstar" a person, the API doesn't let us do this by the standard update action. There's a specific API endpoint for this action:
POST /people/id/star - mark a person as "starred"
POST /people/id/unstar - mark a person as "unstarred"
How do I fit this API in with Ember Data?
It looks like I'd need to extend DS.Store and DS.RESTAdapter somehow, but I'm not sure of the best approach to make them aware of these different actions. It also feels a bit wrong that a generic Adapter for the app has to be aware of starring people.
Note that I have no control over the API, so I can't make POST /people/id aware of "starring" so that it would fit in with a standard update.
Been a while and it may not have been possible at the time, but you can call the ajax method of your adapter directly:
YourApp.Store.prototype.adapter.ajax(url, action, {data: hashOfParams})
For example:
YourApp.Store.prototype.adapter.ajax('/products', 'GET', {data: {ids: [1,2,3]}})
For your question:
YourApp.Store.prototype.adapter.ajax('/people' + id + '/star','POST')
Edit - using buildURL is useful, particularly if you've set a namespace on your adapter:
url = YourApp.Store.prototype.adapter.buildURL('people',id) + '/star'
Edit 2 - You can also get the adapter with container.lookup('adapter:application'), which is useful if you don't have global access to the app (ex ES6 modules / ember-cli)
Edit 3 - The above refers to an outdated version of Ember / Ember-CLI. Now I define this function in a mixin -
customAjax: (method, type, id, action, hash = null) ->
#note that you can now also get the adapter from the store -
#adapter = #store.adapterFor('application')
adapter = #container.lookup('adapter:application')
url = adapter.buildURL(type, id) + '/' + action
hash['data'] = $.extend({}, hash) if hash #because rails
adapter.ajax(url, method, hash).then (result) =>
return result
And call it like so -
#customAjax('PUT', 'modelClass', modelId, 'nonCRUDActionName', optionalHashOfAdditionalData).then (response) =>
#do something with response
I went through the same issue, and solved it using a modified version of what is explained in here:
Non-crud rails actions in Ember.js
Basically, you need to use a Jquery AJAX call, the important part for me was to change the contentType in order to get the auth data to be sent in the request headers, rather than in form-data (the default, if you include data in your call). Also, make sure to give the proper context in the AJAX part, otherwise 'this' won't work in the 'success' callback:
App.ItemController = Ember.ObjectController.extend
actions:
starItem: ->
controller = this
item = #get 'model'
id = item.id
token = #auth.get 'authToken'
$.ajax
type: 'PUT'
context: controller
data: '{ "auth_token": "' + token + '"}'
url: '/api/v1/items/' + id + '/star'
contentType: 'application/json; charset=UTF-8'
dataType: 'json'
success: ->
if #get 'item.starred_by_user'
#set 'model.starred_by_user', false
item.decrementProperty('total_stars')
else
#set 'model.starred_by_user', true
item.incrementProperty('total_stars')
Maybe a good start: https://github.com/beautifulnode/ember.request
Did not tryed it yet, but seems promising in your case...
I think, that in CQRS therms star and unstar are commands. So you should declare model PersonStar and work with it.
In the discussion group, Yehuda mentioned this isn't possible yet.