I have a $http.post request and I understand how angular wraps the $q promises with .success and .error.
Apart from the angular docs and source code I also referred to http://www.peterbe.com/plog/promises-with-$http
Here is my code:
$http.post(url, info).success(onSuccess).error(onError)["finally"](function() {
return spinner.dismiss(); });
This works. But I am not sure if it is the right practice. Are there any situations where this can fail/throw exception? What would be a better way to do this (.then(success,error).finally)?
On further digging I see that the .error is implemented as
function (fn) {
promise.then(null, function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
}
The returned promise allows me to chain a 'finally'. This is the way angularjs has wrapped .error and .success, so chaining a .finally to the block may not be a bad practise.
Related
I'm a bit confused, the more I read resources online about $q and $http the more my head spins. So if I do a $http.get call does that not include a promise? Or do I bring $q in?
It is built on $q and returns a promise. See the docs: https://docs.angularjs.org/api/ng/service/$http
And the example there:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$http.get is just a convenience method on the above.
You can refer to the following link
https://www.peterbe.com/plog/promises-with-$http
This service ($http.get()) will return promise as success callback and error callback...so this function itself returns promise. You just have to handle it
The $http API is based on the deferred/promise APIs exposed by the $q
service. While for simple usage patterns this doesn't matter much, for
advanced usage it is important to familiarize yourself with these APIs
and the guarantees they provide.
https://docs.angularjs.org/api/ng/service/$http
Meaning that the $http.get will return a promise anyway. No need nesting your own $q approach. Just return the $http invoke.
$http.get call does that not include a promise? The Answer is Yes, you can return a promise or resolve the promise with $http
Returning a promise from $http.get
getData: function() {
return $http.get('some url'); // you can resolve this promise later
// using (then)
}
So later in your code you can resolve the above promise like this
...
myService.getData().then(function(response) {
// do something with response
}).catch()
Resolve the promise inline
getData: function() {
$http.get('some url').then(function(response) {
// do something with response
}).catch()
}
How do I reject or fail a successful $http.get() promise? When I receive the data, I run a validator against it and if the data doesn't have required properties I want to reject the request. I know I can do after the promise is resolved, but it seems like a good idea to intercept errors as soon as possible.
I'm familiar with the benefits of $q, but really want to continue using $http.
You can reject any promise by returning $q.reject("reason") to the next chained .then.
Same with $http, which returns a promise - this could be as follows:
return $http.get("data.json").then(function(response){
if (response.data === "something I don't like") {
return $q.reject("bad data");
}
return response.data;
}
This could simply be done within a service, which pre-handles the response with the .then as specified above, and returns some data - or a rejection.
If you want to do this at an app-level, you could use $http interceptors - this is just a service that provides functions to handle $http requests and responses, and allows you to intercept a response and return either a response - same or modified - or a promise of the response, including a rejection.
.factory("fooInterceptor", function($q){
return {
response: function(response){
if (response.data === "something I don't like") {
return $q.reject("bad data");
}
return response;
}
}
});
Same idea as above - except, at a different level.
Note, that to register an interceptor, you need to do this within a .config:
$httpProvider.interceptors.push("fooInterceptor");
You can use AngularJS interceptors. But you still need to use $q in them because $http uses $q.
Here is a useful article about interceptors.
I'm a bit confused how to chain promises in AngularJS. I have a $resource implementation... This is some simplified code to get my point:
myResource
.save({ id: 123 })
.$promise
.then(function(data) {
// redirect to root on success
$location.url("/");
})
.catch(function() {
console.log("Failed");
})
.finally(function() {
// reenable form
$scope.enabled = true;
})
What I'm confused about is whether my .catch() function will be called when my resource will fail saving or will it only catch errors that would originate in .then() function or maybe both?
When chaining these how can I have a single .catch (before .finally) that would catch all errors of calls that happened in the chain proceeding it?
Note: I'm aware I could provide the success and error functions in .save function but I have other parts to handle as well.
First of all, read the docs of Angular's implementation of promises.
Promises can be resolved in two ways - either successful (the resolve method) or not (the reject method).
When resource could not be saved (i.e. server did not respond with 2XX status), then your catch method will be called, as the promise will be rejected.
Also please note, that your catch handler should reject the promise for next possible handlers. When you return non-rejected value from your catch method, it is assumed that the error has been handled and the promise can call subsequent then methods, if any. Therefore, if there is any possibility that the promise is used somewhere else, your code should look like this:
// inject $q
myResource
.save({ id: 123 })
.$promise
.then(function(data) {
// redirect to root on success
$location.url("/");
})
.catch(function() {
console.log("Failed");
return $q.reject();
})
.finally(function() {
// reenable form
$scope.enabled = true;
})
Also notice that you made a typo in the then method - you wrote "ulr" instead of "url".
I would like to make multiple Ajax calls in a chain. But I also would like to massage the data after each call before making the next call. In the end, when All calls are successful, I would like to run some other code.
I am using Angular $http service for my Ajax calls and would like to stick to that.
Is it possible?
Yes, this is handled very elegantly by AngularJS since its $http service is built around the PromiseAPI. Basically, calls to $http methods return a promise and you can chain promises very easily by using the then method. Here is an example:
$http.get('http://host.com/first')
.then(function(result){
//post-process results and return
return myPostProcess1(result.data);
})
.then(function(resultOfPostProcessing){
return $http.get('http://host.com/second');
})
.then(function(result){
//post-process results of the second call and return
return myPostProcess2(result.data);
})
.then(function(result){
//do something where the last call finished
});
You could also combine post-processing and next $http function as well, it all depends on who is interested in the results.
$http.get('http://host.com/first')
.then(function(result){
//post-process results and return promise from the next call
myPostProcess1(result.data);
return $http.get('http://host.com/second');
})
.then(function(secondCallResult){
//do something where the second (and the last) call finished
});
The accepted answer is good, but it doesn't explain the catch and finally methods which really put the icing on the cake. This great article on promises set me straight. Here's some sample code based on that article:
$scope.spinner.start();
$http.get('/whatever/123456')
.then(function(response) {
$scope.object1 = response.data;
return $http.get('/something_else/?' + $scope.object1.property1);
})
.then(function(response) {
$scope.object2 = response.data;
if ($scope.object2.property88 == "a bad value")
throw "Oh no! Something failed!";
return $http.get('/a_third_thing/654321');
})
.then(function(response) {
$scope.object3 = response.data;
})
.catch(function(error) {
// this catches errors from the $http calls as well as from the explicit throw
console.log("An error occured: " + error);
})
.finally(function() {
$scope.spinner.stop();
});
Could you explain in general what does this code do:
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('HttpSpinnerInterceptor');
$httpProvider.defaults.transformRequest.push(function (data, headersGetter) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.gif') %>");
return data;
});
}]);
App.factory('HttpSpinnerInterceptor', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return response;
}, function (response) {
angular.element('.brand img').attr("src","<%= asset_path('brand/brand.png') %>");
return $q.reject(response);
});
};
});
I have completely no understanding except some guesses that it intercepts some response and injects a src attribute of image.
I do not understand how and when is HttpSpinnerInterceptor called and what the "promise" parameter is.
HttpSpinnerInterceptor is been called after each request issued by using $http service is completed (successfully or not), but before promise is been resolved to caller (so you can defer result). Actually transform request is not needed, because it does mostly same as HttpSpinnerInterceptor (or HttpSpinnerInterceptor is not needed...), because it does not transform anything.
promise parameter is a $q promise that could be used in case if you need to perform some async actions when with result of your request as you can resole it later, so caller would get result later. Actually in your code, you directly resolve this promise (or reject it), changing src attribute of the image.
Here are some links to documentation:
Using $http service: http://docs.angularjs.org/api/ng.$http - take careful look at "Response interceptors" and "Transforming Requests and Responses"
Promises in AngularJS: http://docs.angularjs.org/api/ng.$q