This seems like it should be dead simple. I want to use $resource to save() something to the server. The server will respond with a cookie. I need to use that cookie on my next request. Somehow, in the year 2014, it seems that this is literally impossible.
The $resource looks something like this:
cartService.factory('AddToCart', ['$resource', function($resource) {
return $resource('my-url.com'
{
withCredentials: true,
transformResponse: function(data, headersGetter) {
alert(headersGetter);
}
}
);
}]);
First of all, the transformResponse isn't firing at all. I figured I might be able to breakpoint it and look at the backing object... nope.
Second, the only header there seems to be the content-type header. Everything from Date to X-* to transfer-encoding seems to be stripped out.
If I add a success() function to the call, it looks like this:
$scope.order = AddToCart.save({}, function(a, b) {
console.log(a);
console.log(b('JSESSIONID'));
});
Again, nothing. I'd really appreciate any pointers. If I have to dig into the $http guts just to deal with cookies, I can't help but be confused by the point of this framework.
Related
We will soon be refactoring our code against the Angular Style Guide. The guide itself is great (and can be found slightly modified all over the interwebs), but no one mentions how $resource fits into a factory, or any reasons why it might have been left out. One guide says to use $resource over $http where you can, but then doesn't add it into their style for factories :/.
I remember reading in lots of places that $resource was better and that's why I started to use it, but now I'm forgetting why and wondering if that is still true - especially given the resource object at the bottom of this post. There are some opinions (Papas own, and again) about $resource (not?) being great, but that's another issue that I'm re-checking.
So, assuming we want to use $resource and given this sample code below, where does $resource fit in so that it adheres to the reasoning behind the styles in the guide? Also, if your answer is "It doesn't. The style [subtly] recommends $http because bla, bla and bla.", then that would be a useful as well.
(function() {
'use strict';
angular
.module('myModule')
.factory('oneService', oneService);
predicateService.$inject = ['twoService', 'anotherService'];
/* #ngInject */
function oneService(twoService, anotherService) {
var service = {
doSomething: doSomething,
etc: etc
};
// pos 1 (it really only works here but can be LONG)
// var fancyResource = $resource('/path/to/thing', '...');
// Ideally, this should be kept close to the top, right?
return service;
// pos 2 (here or below ////// is cleaner, but doesn't work)
// var fancyResource = $resource('/path/to/thing', '...');
////////////////
function doSomething() {}
// rest of functions here etc...
}
})();
Now, the only place that we use $resource (and maybe this is also incorrect) is within methods like doSomething(). At various points in the past, and even in various places in our code today, fancyResource is made public by the service and used directly from the controller: oneService.fancyResource.get(). I'm thinking this may be the intended use for $resource, but I'm not sure anymore.
Also, consider that one service might be quite large (never mind the fact that some of this should/could be broken into multiple resources; let's just pretend a resource object this size is likely and many verbs are needed):
var userResource = $resource(baseApiPath + 'users', {}, {
get: {
method: 'GET',
headers: utilityService.getHeaders('sampling'),
isArray: true,
transformResponse: function(response){
response = JSON.parse(response);
if(response.result){
return response.result.users;
}
return response;
}
},
getUserDetails: {
method: 'GET',
url: baseApiPath+'users/:userId',
params: {
userId: '#userId'
},
headers: utilityService.getHeaders('sampling'),
transformResponse: function(response){
response = JSON.parse(response);
if(response.result){
return response.result.user;
}
return response;
}
},
getUserByRole: {
method: 'GET',
url: baseApiPath+'users/roles/:roleId',
params: {
roleId: '#roleId'
},
headers: utilityService.getHeaders('sampling'),
},
getLoggedInUserData: {
method: 'GET',
url: baseApiPath + 'users/userData',
headers: utilityService.getHeaders('sampling'),
},
getGrantedAuth: {
method: 'GET',
url: baseApiPath+'users/applicationPermissions/userId/:userId/:applicationId/',
params: {
applicationId: '#applicationId',
userId: '#userId'
},
headers: utilityService.getHeaders('sampling'),
}
});
So, I think I've found my answer based on a few thoughts.
Firstly, I now realize that using a $resource like this is totally incorrect for two reasons. The first is that I was creating additional actions that required their own unique path. The whole point of a $resource is to make doing GET, PUT, POST, DELETE on a single REST resource easier. I was basically combining my resources because they appeared to be unified. For example, /users and /users/roles/:roleId should have been two different resources (and probably put into two different services to maintain the single responsibility style).
The second way I was using $resource wrong is actually because I wasn't really using the query, save, or delete methods that it supplies me with. I would just create another custom action for whatever I wanted to do. Sometimes this also included a unique URL like /users/:userId/delete, and that was because the API wasn't always a REST API. $resource is specifically designed for REST compliant APIs. Because it wraps $http and it can pass parameters to it, it's easy to fall into this trap. $resource is not intended to be a configuration for multiple $http uses.
So, now with that out of the way, here is how I would propose to include $resource into a factory, and still follow the style guide.
First, $resource should only be used with a true REST API. One where you only have/need one path, and only/mostly HTTP methods are used to interact with it. Also, because a factory is intended to represent and manage one kind of 'thing', interacting with the 'thing API', there should really only be one $resource per service. Extending the example, there would be a users service and a roles service; each with one $resource. There could then be another userRoleService that utilizes them both, and doesn't actually do any $resource stuff on its own. Something like that, anyway.
This being the case, the $resource config would actually be significantly shorter than what I was originally posting. Since it's smaller, we can treat it more like a variable declaration and put it above the service object that we create.
(function() {
'use strict';
angular
.module('myModule')
.factory('oneService', oneService);
predicateService.$inject = ['anotherService', '$resource'];
/* #ngInject */
function oneService(anotherService, $resource) {
// this resource is unlikely to get longer than this
var userResource = $resource('http://api.com/users/:userId', {
userId: '#userId'
});
// and we're still able to see all bindables at the top
var service = {
doSomething: doSomething,
etc: etc
};
return service;
////////////////
function doSomething() {
// and in a service method, we can use the resource like this,
userResource.query().$promise
.then(function(response){...})
}
function doSomethingElse() {
// or we could use the $resource in a way that would make
// chainable with another .then() in the calling method.
var load = userResource.query();
// do some internal stuff when we get the response
load.$promise
.then(function(response){...});
// then return the $resource object, or load.$promise
// so that another .then can be chained afterwards.
return load;
}
// rest of functions here etc...
}
})();
Anyway, that's the answer that I came up with. I hope this helps some of you who came here looking for what I was looking for (and couldn't easily find).
I'm trying to mock a $http get request with parameters. Using the actual $http service we could do something like this
$http.get(url, {params : { articleId:10, id : 1}})
.success(function (response) {
defer.resolve(response.data);
});
Can we do the same with $httpBacked?
$httpBackend.when('GET', url, { params: { articleId: 10, id : 1}})
.respond({data: areas});
The obvious unpleasant alternative is writing out the full expected url which I am hoping I don't have to do as this feels neater.
Thanks
I had forgotten about this question, unfortunately the answer is that you have to specify the full get url with parameters
....just incase someone stumbles across this with the same problem
Apparently the only thing that can be passed parameters in a regular expression. According to the documentation of angular only four parameters can be passed, method, url, data, and headers.
I'm having some trouble with error handling in a little angularjs application. I'm interacting with a Flask backend and a Postgres DB.
I have a factory service
appointServices.factory('Appointments', ['$resource', function($resource){
return $resource(someUrl, {}, {
query: { ... }
,
create: {
method: 'POST'
,url: 'http://somedomain:port/new/:name/:start/:end/:treatment'
,params: { start: '#start', end: '#end', name: '#name', treatment: '#treatment' }
,isArray:false
}
});
}
]);
Inside a controller I'm making the following call
Appointments.create($scope.appointment, function(value, responseHeaders) {
// success handler
console.debug('success: ', JSON.stringify(value));
}, function(httpResponse) {
// error handler
console.debug('error: ', JSON.stringify(httpResponse));
});
Here $scope.appointment contains the relevant parameters for the create action.
Now, in the backend I'm able to catch DB errors involving constraints and I'm trying to return an error code with a 'meaningful' message. So I have a python method
def create(name, start, end, treatment):
try:
...
transaction_status = 'ok'
code = 200
except IntegrityError as e:
...
transaction_status = 'IntegrityError'
code = 500
finally:
...
return make_response(transaction_status, code)
Everything works fine, I'm able to talk to the backend, create new data and insert this in the DB. As I said, any violation of the constraints is detected and the backend responds
curl -X POST "http://somedomain:port/new/foo/bar/baz/qux" -v
...
< HTTP/1.0 500 INTERNAL SERVER ERROR
...
IntegrityError
So, the problem is, no matter whether the action create was successful or not, the intended error handler specified inside the controller is always fired. Moreover, I always end up with a status code 404 in the httpResponse. Firebug shows correctly the code 500 as above, though.
Anybody has any idea of why I'm getting this behavior?
Any suggestions on how to improve the error handling mechanism are also welcome.
Thx in advance.
P.S. Following the documentation on $resource I have also tried variations on the factory service call, e.g.
Appointments.create({}, $scope.appointment, successCallback, errorCallback);
Appointments.create($scope.appointment, {}, successCallback, errorCallback);
with the same results.
Update:
Forgot to mention the important fact that I'm interacting with the backend via CORS requests. The POST request in create above is having place with the OPTIONS method instead. As I mentioned everything is working correctly except for the error response.
Under further investigation, I tried to isolate the factory service, in case I did something wrong, and I also tried the approach shown in the credit card example ($resource docs), but with no positive result.
However, I came up with two workarounds. Firstly, I was able to create a plain JQuery POST request, as in the example shown in the docs. This time, the request is not replaced by OPTIONS and I got the error code correctly.
I also managed to connect to the backend with the low-level $http service as follows:
var urlBase = 'http://somedomain:port/new/:name/:start/:end/:treatment';
var url = urlBase.replace(/:name/g, $scope.appointment.name);
url = url.replace(/:start/g, $scope.appointment.start);
url = url.replace(/:end/g, $scope.appointment.end);
url = url.replace(/:treatment/g, $scope.appointment.treatment);
// force method to be POST
var futureResponse = $http({ method: 'POST', url: url });
futureResponse.success(function (data, status, headers, config) {
console.debug('success: ', JSON.stringify(data));
});
futureResponse.error(function (data, status, headers, config) {
console.group('Error');
console.debug(JSON.stringify(status));
console.debug(JSON.stringify(data));
console.groupEnd();
});
This time, as in the case of JQuery, the request is done effectively with POST and error codes are correctly received.
Notice also that I'm not calling $http.post but I set the method to POST as part of the object parameter to $http, otherwise the connection takes places with OPTIONS as before.
Still trying to figure out what is happening with $resource.
I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});
When I instantiate the following code in an AngularJS app, I get weird data in the transformResponse function (bottom of code). I'm not calling the $resource function from any controller, just loading the script in a browser. The data variable (see code) contains the HTML of the current partial, when loading the app.
This seems odd. Is this the way it's supposed to be?
var buddyServices = angular
.module('buddyServices', ['ng','ngResource'])
.factory('Buddy',
function ($resource) { console.log('resource');
return $resource('http://webservice.buddyplatform.com/v1/:service',
{service:'', BuddyApplicationName: 'xxx',
BuddyApplicationPassword: 'yyy'}
);
}
)
.config(function($httpProvider){
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.transformResponse = function(data) {
console.log(data);
return 'TEST: '+data;
};
});
=== EDIT ===
It just daunted on me: $httpProvider handles all http requests, so a page load is one of those. I'm guessing a bit now, but it seems probable. So, the question then becomes: Is there an "easy" way to constrain the data in the code above to only those requests performed by my service?
transformResponse takes another parameter headersGetter. You can use this to get the headers send with the response. Look for Content-Type header header. It should contain application/json