I want to make a HTTP request to my server and can't find the answer on my question how to make .always() like in jQuery style.
According Angular's documentation of $http, there is only this construction:
// Simple GET request example :
$http.get('/someUrl').
then(function(response) {
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
There's the finally():
$http.get('/someUrl').
then(function(response) {
// this callback will be called asynchronously
// when the response is available
}.catch(error) {
// called asynchronously if an error occurs
// or server returns response with an error status.
}).finally() {
//Always do something
});
Which will always be called no matter what the outcome is.
You can read about this in the documentation for $q here.
Related
I have a function that looks like this in an angular controller:
$http.get(dataUrl)
.then(function (response) { // success getProducts
$scope.data.products = response.data;
})
.catch(function (error) {
$scope.data.error = error;
});
dataUrl is a constant and when correct returns a list of products and everything works.
Next I wanted to test the error handling.
So I changed the dataUrl to an incorrect port number to get my data.
Now my error div shows and my content div hides.
This is correct functionality.
Here is my problem.
In my div when I show error.status, it comes back as -1. The example I am following says it should be 404. Also my statusText is empty or "".
Why is my error object not populating with anything.
I originally had this set up with error function as the second callback to then().
But I get the same results either way and I think catch() is cleaner as eluded to in another posted question on stack.
From https://docs.angularjs.org/api/ng/service/$http
A response status code between 200 and 299 is considered a success
status and will result in the success callback being called. Any
response status code outside of that range is considered an error
status and will result in the error callback being called. Also,
status codes less than -1 are normalized to zero. -1 usually means the
request was aborted, e.g. using a config.timeout. Note that if the
response is a redirect, XMLHttpRequest will transparently follow it,
meaning that the outcome (success or error) will be determined by the
final response status code.
The correct URL is: "http://localhost:5125/products" (as in:
angular.module("sportsStore")
.constant("dataUrl", "http://localhost:5125/products1")
.controller("sportsStoreCtrl", function ($scope, $http, dataUrl) {
$scope.data = {};
$http.get(dataUrl)
.then(function (response) { // success getProducts
$scope.data.products = response.data;
})
.catch(function (error) {
$scope.data.error = error;
});
});
If I change it to: "http://localhost:5000/products", the ajax request is no longer going to a webserver because it cannot find deployd serving up the products collection on port 5000. So the comment from JB Nizet is correct above.
There is no server to send back 404 or 500 so the default status must be -1 and the default statusText must be "".
But if I try this: http://localhost:5125/products1, then the ajax request finds deployd, but sends back 404 because there is no products1 collection or products1 API and so no get method for a products1 collection. So deployd sends back 404 in this case:
I have this code:
dogsResource.delete({id: $stateParams.dogId}, angular.noop,
function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
The delete is done, the problem is that neither success nor error is being called. I've also tried using an instance (that means, to use $delete), but it didnt work either.
I tried testing the callbacks with other methods, such as get
$scope.dog = dogsResource.get({id: $stateParams.dogId}, function(value, res){
console.log(value);
});
And it works. I don't know why that happen, since the dog is being deleted from database.
Thanks
UPDATE
dogResource code
// Return the dogs resource
.factory('dogsResource', ['$resource', function($resource){
return $resource("http://localhost:5000/dogs/:id",{id: "#id"},{update: {method: "PUT"}});
}])
UPDATE 2
I Found the error. It was in the RESTful API (Node js). The method was not sending anything to Angular, so no callback was triggered:
//DELETE - Delete a dog with specified ID
exports.deleteDog = function(req, res) {
console.log('DELETE' + req.params.id);
Dog.findById(req.params.id, function(err, dog) {
dog.remove(function(err) {
if(err) return res.status(500).send(err.message);
console.log('Succesfully deleted.');
res.status(200);
})
});
};
Replacing res.status(200) with res.status(200).end() got the callback triggered.
Thanks you all for your time.
I suggest to you to not use
res.status(200).end()
In fact usually when you delete an object with a REST service in expressJS, the common case is to send the deleted object as response, because it could be useful for the frontend to get this object (and to make sure that it's the good object).
So instead of use
res.status(200).end()
use
res.send(dog)
Or if you want to send an empty response, the status code for a delete operation should be :
res.status(204).end()
204 NO CONTENT
Note that you don't need to set the status code by default it will be 200. So set status code to 200 is just useless.
And to finish an http response needs to be sent to close the request. The end method or the send method make that. Set a status code to a response http will never send anything to the frontend. That's why your angular callback was never fired.
So i suggest to you to add the tag expressjs to your question, because it's not an AngularJS problem but a real expressJS mistake.
In your code, the second argument is angular.noop:
dogsResource.delete({id: $stateParams.dogId}, angular.noop,
function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
According to the ngResource Source Code, if you set the second argument to a function (angular.noop is a function) then it will use the second argument as the success callback. Since the second argument is a no-operation, nothing will happen when it is called.
Try setting the second argument to function (r) { console.log (r) } and see what you get.
I'm recently working with ngResource. In my case, I've have used three parameters in that api call. Therefore, you could use
dogsResource.delete({id: $stateParams.dogId}, function(value, responseHeaders){
//Success
console.log(value);
console.log(responseHeaders);
},
function(httpResponse){
//Error
console.log(httpResponse);
}
);
I hope that helps.
Use promise return by the $resource object. As $resource object by default return a promise object, and that promise object is available .$promise variable over that $resource API method.
Code
dogsResource.delete({id: $stateParams.dogId}).$promise.then(function(data)//Success
console.log(value);
},
function(httpResponse){ //Error
console.log(httpResponse);
});
I am building an authentication solution with Angular. I am using Interceptors to validate Request and if there is not valid token prevent from further processing and redirect. Here is my simplified Interceptor:
prism.service('APIInterceptor', function($rootScope) {
var service = this;
service.request = function(config) {
.....
return config;
};
})
Just for the sake of POC that I am working on what would the correct way of stopping this request from any further processing be?
Thanks
From the Angular Docs on Interceptors for the request method:
request: interceptors get called with a http config object. The function is free to modify the config object or create a new one. The function needs to return the config object directly, or a promise containing the config or a new config object.
The rest of the documentation can be found here. From this you can see that the method can also return a promise (which is actually pretty awesome) so you could always reject it.
Try something like this:
prism.service('APIInterceptor', function($q, $rootScope) {
this.request = function(config) {
if( /*config is not valid*/ ) {
return $q.reject({message: 'ERROR, ERROR... INTRUDER ALERT!', status: 401, config: config});
} else {
return config;
}
};
});
And see how it might be handled (I have no idea what your application will do). Let me know if it works out for you!
EDIT: My answer has been accepted, but is incomplete and it will haunt me forever if I don't complete it. So, after writing some test code of my own I've realized that you can do 1 of 2 things in this situation. The first is to handle the unauthorized request in the interceptor:
...
this.request = function(config) {
if(/* config is not authorized */) {
// Do something here like redirect/issue another request... whatever
return $q.reject({/*whatever the hell you want*/});
} else ...
};
...
This obviously works best if you want to handle all unauthorized requests the same. If you don't, however, the second option is to defer to the service that issued the request. For example, if you're using $http you can do this:
$http.get('/words/words/words/').then(function(){
// This is where you handle a successful request.
}, function(error) {
// Handle your error here. Please take note that this error message is
// whatever you sent back in the `reject` previously
});
Hopefully that clears a few things up.
I need to add some data to each response I send via $http in angular that will be in the response. In other works I'm trying to add an 'id' to the request because when the response is returned I need to associate it with the correct object that sent it. Is this possible? If so how would I go about it?
use interceptors, I'm quoting from the documentation:
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests.
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
'request': function(config) {
config.id = generateId(); //or a timestamp maybe?
return config;
},
'response': function(response) {
// do something on success
return response;
}
};
});
then add your
$httpProvider.interceptors.push('myHttpInterceptor');
I have an issue with angularjs app communicating with a REST API. The problem is the api returnes a status code and also adds a status description like HTTP/1.1 417 Invalid quantity.
With jquery ajax the jqXHR object had a statusText property but with angularjs I can't seem to find how can I access this in my error handler. Needless to say I can't modify the API which for a 417 status code for example can return different status descriptions.
I have found that I would need to change the angularjs library but in case of an update this would not be handy. I would have to change xhr.responseText to xhr.statusText. Can I somehow overwrite this function from my code and don't modify angular library?
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
completeRequest(
callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
}
};
Here's the work around to get StatusText (as pointed out that some mobile browsers strip out DATA from successfull responses: https://stackoverflow.com/a/28470163/1586498
I have also discovered that .success and .error on $http in Angular does not pass the StatusText, you have to use .then(response)
You need to create an httpInterceptor as outlined here during your module config.
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q) {
return function(promise) {
return promise.then(function(response) {
// do something on success
}, function(response) {
//do something for your 417 error
if(response.status == 417) {
alert("We've got a 417!");
return $q.reject(response);
}
//some other error
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');
The support for statusText was added in AngularJS 1.2.16.