I have a Api in which retrieves a custom header: X-Total-Count : "total of items", I'm using angular with ngResource.
My factory look like this:
app.factory("shopFactory", function ($resource) {
return {
User: $resource("http://localhost:58495/users/api/User/?id=:id", { id: "#id" }),
Category: $resource("http://localhost:58495/category/:id", { id: "#id" }),
Product: $resource("http://localhost:58495/products/?from=:from&to=:to", { from: "#from", to: "#to" })
};
});
And when I call it:
var productServer = shopFactory.Product.query({ from: 0, to: 10 }).$promise.then(function (response) {
$scope.product = response;
console.log(response);
}, function (error) {
console.log("ERROR");
console.log(error);
});
How can I access my custom header through ngResource, I can access it but with $http, I want to do it with the $resource way, Thanks
The query action method can be called with three arguments:
Resource.query([parameters], [success], [error])
The success callback is called with (value (Object|Array), responseHeaders (Function), status (number), statusText (string)) arguments, where the value is the populated resource instance or collection object. The error callback is called with (httpResponse) argument.
var productServer = shopFactory.Product.query(
{ from: 0, to: 10 },
function success (value, headers, status, statusText) {
$scope.product = value;
console.log(value);
console.log(headers());
},
function error (error) {
console.log("ERROR");
console.log(error);
}
);
For more information, see AngularJS $resource Service API Reference.
whats the difference between using $promise.then and the success function,
The function in the .then method exposes only the value of the final response. While the success callback exposes four arguments: value (Object|Array), responseHeaders (Function), status (number), statusText (string).
The $promise can be passed as an argument to other functions and its .then method can be invoked multiple times.
Another very important difference is that the .then method creates a new promise from values returned.
Chaining promises
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
— AngularJS $q Service API Reference (Chaining Promises)
Related
What is the use of $q.defer() in following code. I am bit confused or not able to understand the use of $q or defer?
service.serviceCall = function (methodName, params) {
var deferred = $q.defer();
$http({ method: "POST", url: url + methodName, data: params, headers: headers }).success(function (result) {
deferred.resolve(result);
}).error(function (result) {
deferred.reject(result);
});
return deferred.promise;
}
service.serviceCal("POST", {"param1":"value1"}).then(function(data){
//here data will be object which is resolved in success call
}).fail(function(){
//here data will be object which is rejected in failure call
})
This are promises, we user $q.defer() to return a promise using defer.promise(). It is contract between the calling object and promise that in future the calling object (here then and fail) will ultimately get a value either in then or fail depends whether it is resolved or rejected.
Read about promises here: Promises
Go through docs of $q module: $q Module Angular
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing.
So promises are used in async programming. $q is Angular's implementation of promises.
Usage of the above function:
service.serviceCal(methodName, params)
.then(function(resolve){
// on successful resolving
// called when defer.resolve is called
// resolved object: resolve
}, function(reject){
// on reject
// called when defer.reject is called
// rejected object: reject
})
The code is using deprecated methods on the result of calling $http. Although $http returns a promise it has some additional methods .success and .error that don't quite work within the usual promise structure.
$q.defer() creates a promise which is returned from the function and this code uses success and error to resolve the promise. A simpler way to write this code would be to just use the promises returned by $http and its .then method.
service.serviceCall = function (methodName, params) {
return $http({ method: "POST", url: url + methodName, data: params, headers: headers })
.then(function successFn (response) {
return response.data;
});
}
This has the same effect as the original code: it returns a promise which either resolves to the data from the response, or if an error occurs the promise is rejected.
$scope.tempObject = {};
$http({
method: 'GET',
url: '/myRestUrl'
}).then(function successCallback(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
}, function errorCallback(response) {
});
console.log("Temp Object outside $http ", $scope.tempObject);
I am getting response in successCallback but
not getting $scope.tempObject outside $http. its showing undefined.
How to access response or $scope.tempObject after $http
But if I want to use $scope.tempObject after callback so how can I use it. ?
You need to chain from the httpPromise. Save the httpPromise and return the value to the onFullfilled handler function.
//save httpPromise for chaining
var httpPromise = $http({
method: 'GET',
url: '/myRestUrl'
}).then(function onFulfilledHandler(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
//return object for chaining
return $scope.tempObject;
});
Then outside you chain from the httpPromise.
httpPromise.then (function (tempObject) {
console.log("Temp Object outside $http ", tempObject);
});
For more information, see AngularJS $q Service API Reference -- chaining promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Explaination of Promise-Based Asynchronous Operations
console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
console.log("Part3");
});
console.log("Part4");
The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.
Demo
console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
console.log("Part 3");
});
console.log("Part *4*");
Additional Resources
Angular execution order with $q
What is the explicit promise construction antipattern and how do I avoid it?
Why are angular $http success/error methods deprecated? Removed from v1.6?
How is javascript asynchronous AND single threaded?
Ninja Squad -- Traps, anti-patterns and tips about AngularJS promisesGood theory but needs to be updated to use .then and .catch methods.
You're Missing the Point of Promises
$http call is async call. The callback function executes when it has returned a response. Meanwhile the rest of the function keeps executing and logs $scope.tempObject as {}.
When the $http is resolved then only $scope.tempObject is set.
Angular will bind the changed value automatically using two way binding.
{{tempObject}} in the view will update itself.
if you want to use tempObject after callback then do this
then(function(data){
onSuccess(data);
},function(){
});
function onSuccess(data){
// do something
}
Try to use a $scope.$apply before to finish the successCallback function. An other solution is to change successCallback -> function so:
$http({ method: 'GET', url: '/myRestUrl' }).then(function(success) { $scope.tempObject = success; console.log("Temp Object in successCallback ", $scope.tempObject); }, function(error) { });
I am starting to learn AngularJS $resource, and noticed the $resource object has a few methods (see below for examples) attached to my data downloaded from server. How do I remove these methods and convert the object to a regular (array) object?
__proto__: Resource $delete: function (params, success, error) {$get: function (params, success, error) {$query: function (params, success, error) {$remove: function (params, success, error) {$save: function (params, success, error) {constructor: function Resource(value) {toJSON: function () {__proto__: Object
For instance, I'm trying to send a POST request including some key value data using $resource.save, but these 'proto' items in the array is somehow causing the data to become "undefined" when passed to $.param(data) in the factory. I could do the same thing using $http with ease, but want to learn $resource. Thanks!
Inside a Controller
$scope.ok = function () {
$scope.entry = new calEntry();
$scope.entry.data = data // data is $resource object including _proto_ items
$scope.entry.$save( function(){
toaster.pop('success','Message','Update successfully completed.');
});
};
Factory
myApp.factory("calEntry",['$resource','$filter', function($resource, $filter) {
return $resource("/griddata/", {}, {
save: {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
transformRequest: function(data, headersGetter) {
return $.param(data); // data is undefined when it reaches here
}
}
});
}]);
Try the toJSON function, it will fetch the data and remove the extra properties.
I'm willing to retrieve the response header of a resource request, cause I've put pagination information and something else in it rather than the response body, to make the REST api clear.
Though we can get it from the success / error callback like below:
Object.get({type:'foo'}, function(value, responseHeaders){
var headers = responseHeaders();
});
Where 'Object' is my resource factory service.
Further, when I'm trying to make the route change after required resources resolved, I've tried this:
.when('/list', {
templateUrl: 'partials/list.html',
controller: 'ListCtrl',
// wait for the required promises to be resolved before controller is instantialized
resolve: {
objects: ['Object', '$route', function(Object, $route){
return Object.query($route.current.params).$promise;
}]
}
})
and in controller, just inject "objects" instead of Object service, because it's resolved and filled in with real data.
But I got problem when I try to get headers info from the "objects" in controller.
I tried objects.$promise.then(function(data, responseHeaders){}), but responseHeader was undefined.
How can I change the $resource service's behavior so that it throws the responseHeader getter into the $promise then() callback function?
My service "Object" for reference:
myServices.factory('Object', ['$resource',
function($resource){
return $resource('object/:id', {id: '#id'}, {
update: {method: 'PUT'},
});
}
]);
I had the exact same problem. I used an interceptor in the resource definition to inject the http headers in the resource.
$resource('/api/resource/:id', {
id: '#id'
}, {
index: {
method: 'GET',
isArray: true,
interceptor: {
response: function(response) {
response.resource.$httpHeaders = response.headers;
return response.resource;
}
}
}});
Then, in the then callback, the http headers are accesible through $httpHeaders:
promise.then(function(resource) {
resource.$httpHeaders('header-name');
});
I think I had a similar problem: After POSTing a new resource I needed to get the Location header of the response, since the Id of the new resource was set on the server and then returned via this header.
I solved this problem by introducing my own promise like this:
app.factory('Rating', ['$resource',
function ($resource) {
// Use the $resource service to declare a restful client -- restangular might be a better alternative
var Rating = $resource('http://localhost:8080/courserater/rest/ratings-cors/:id', {id: '#id'}, {
'update': { method: 'PUT'}
});
return Rating;
}]);
function RestController($scope, $q, Rating) {
var rating = new Rating();
var defer = $q.defer(); // introduce a promise that will be resolved in the success callback
rating.$save(function(data, headers){ // perform a POST
// The response of the POST contains the url of the newly created resource
var newId = headers('Location').split('/').pop();
defer.resolve(newId)
});
return defer.promise;
})
.then (function(newId) {
// Load the newly created resource
return Rating.get({id: newId}).$promise; // perform GET
})
.then(function(rating){
// update the newly created resource
rating.score = 55;
return rating.$update(); // perform PUT
});
}
We can't use .then for returning the header because the promise doesn't allow for multiple return values. (e.g., (res, err))
This was a requested feature, and was closed https://github.com/angular/angular.js/issues/11056
... the then "callbacks" can have only [one] argument. The reason for this is that those "callbacks" correspond to the return value / exception from synchronous programming and you can't return multiple results / throw multiple exceptions from a regular function.
I'd like to make subsequent ajax calls in angularJs inside a service.
I've tried with something like this:
var requests = [
{'url': 'call1'},
{'url': 'call2'},
{'url': 'call3'}
];
return $q.all([
$http({
url: requests[0],
method: "POST"
}).then( /*callback*/ ),
$http({
url: requests[1],
method: "POST"
}).then( /*callback*/ )
]);
But this make alla ajax in parallel. I need a way to make this calls subsequent, so after first end, it calls second....
You can chain promises:
var requests =[{'url':'index.html'},
{'url':'index.html'},
{'url':'index.html'}];
function makeCall(n) {
return $http({url:requests[n].url+"?n="+n,method:"GET"}).then(function(r) {
if (n+1<requests.length) return makeCall(n+1);
});
}
makeCall(0);
http://plnkr.co/edit/1HdYUtHKe8WXAFBBq8HE?p=preview
You could use async.eachSeries:
var requests = ['call1', 'call2', 'call3'];
function iterator(request, done) {
$http({
url: request,
method: "POST"
}).then(done);
};
async.eachSeries(
requests,
iterator,
function (err) {
// Done
}
);
From the readme:
eachSeries(arr, iterator, callback)
The same as each only the iterator is applied to each item in the array in series. The next iterator is only called once the current one has completed processing. This means the iterator functions will complete in order.
arr - An array to iterate over.
iterator(item, callback) - A function to apply to each item in the array. The iterator is passed a callback(err) which must be called once it has completed. If no error has occured, the callback should be run without arguments or with an explicit null argument.
callback(err) - A callback which is called after all the iterator functions have finished, or an error has occurred.
You should be able to call another $http call in the "then" callback, returning the return value of $http
$http({...})
.then(function() {
return $http({...});
})
.then(function() {
return $http({...});
});
This works because each call to $http returns a promise. If you return a promise in the "then" success callback, then the next "then" callback in the chain will be deferred until that promise is resolved, which will be when the ajax call completes.
Edit: In response to the comment, you can loop over an array of requests:
var requests = [
{'url':'call1','method':'get'},
{'url':'call2','method':'get'},
{'url':'call3','method':'get'}
];
var promise = null;
angular.forEach(requests, function(details) {
promise = $q.when(promise).then(function() {
return $http(details);
});
});
As in the Plunker at http://plnkr.co/edit/RSMN8WuPOpvdCujtrrZZ?p=preview . The $q.when is just for the first value of the loop, when promise is set to null, so it has a then callback that is called immediately.