angularjs $http cache, will it cache server errors - angularjs

In angular $http.get(url, {cache:true}) will nicely cache server responses. But what does the cache do when the GET request to the server fails? This could be a temporary connection issue or a server response like a timeout.
Suppose I handle the error and display a retry button, will a subsequent call to $http.get(url, {cache:true}) retry the server or will it give me a failed promise from the cache?

If multiple requests are made to the same GET url the cache will cause there to only be one request. Quoting the documentation:
If there are multiple GET requests for the same URL that should be cached using the same cache, but the cache is not populated yet, only one request to the server will be made and the remaining requests will be fulfilled using the response from the first request.
However, when that request returns if the status code is not successful it will not be cached (it will be removed from the cache).
We can see this in the source code:
/**
* Callback registered to $httpBackend():
* - caches the response if desired
* - resolves the raw $http promise
* - calls $apply
*/
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}
So in short - Angular will not cached 'failed' requests - that is: requests with a non-success status code.

Related

How to pass variable from app.config() to app.run()

I am loading data (idle time and timeout time) from the database via rest api call. From frontend side I am using AngularJs 1.3.1 $http get call to get the data.
I need the above data (idle time and timeout time) at the app.config() level --- which is working fine.
I need to pass the same data (idle time and timeout time) from app.config() to app.run(). Any idea how to do that?
Also how to make sure $http get call is completed and idle time and timeout time is fetched from the database before idle time and timeout time is sent to app.run()?
I hope people will understand the question and respond to it. I am stuck at it right now.
code block:
angular.module().config(function() {
var idleWarnTime, idleTimeOut;
var http = angular.injector([ 'ng' ]).get('$http');
http.get('timeout').success(function(response) {
idleWarnTime = response.data.warntime;
idleTimeOut = response.data.timeout;
}).error(function(error) {
console.log('session timeout details fetching from db failed');
});
});
angular.module().run(function() {
//need idleWarnTime and idleTimeOut values here and only after "timeout" rest api provide the result
});

$http service cache when the method is post

when I set the $http to cache requests, I still see duplicate requests (with the same url and same data) sent to the server from browser network,
$http.post(url, data, {cache:true} ).success(function(response) {
I have following questions:
Is this a right behaviour?
Can we cache post requests?
Is this the right way to do so or should I be doing it manually with the $cachefactory ?
From the docs:
Only GET and JSONP requests are cached.
If you want to cache POST-requests you would have to do it manually. You will need to make a service/factory that caches responses and serves as a layer before $http. You can use $cacheFactory or just a plain object.
function cacheService($http, $q){
var cache = {};
this.callSomething = function(postData){
let deferred = $q.defer();
let hash = angular.toJson(postData);
if(cache[hash]){
deferred.resolve(cache[hash]);
} else {
$http.post('path/to/resource', postData).then(function(response){
cache[hash] = response;
deferred.resolve(response);
});
}
return deferred.promise;
}
}
This is a simple example, you could of course use the same principle and make a more generalized service that takes an URL, postData and a cache object and returns a function that does the request and caches it.
I am not sure about cache working. But you can use $cacheFactory for same.
app.factory('Cache', function ($cacheFactory) {
return $cacheFactory('Cache');
});
app.controller('MyController', function ($scope, $http, Cache) {
$http.post(url, data, {cache:Cache} ).success(function(response) {}
});
EDIT:
Only GET and JSONP requests are cached.
The cache key is the request URL including search parameters; headers are not considered.
Cached responses are returned asynchronously, in the same way as responses from the server.
If multiple identical requests are made using the same cache, which is not yet populated, one request will be made to the server and remaining requests will return the same response.
A cache-control header on the response does not affect if or how responses are cached.
AngularJS documentation mentions that:
Only GET and JSONP requests are cached.
$http.get(url, {cache: true}) caches the HTTP response in the default cache object (created with $cacheFactory).
Items on the $cachefactory are stored as key-value pairs. The url specified on the $http object is used as the key for the cached value (to be returned). This is one of the reasons it works well with GET which only depends on the URL being hit.
In case of a POST request, the data being sent will also affect the response besides the URL being hit which makes caching a POST request much more complex (since the request will also have to become a part of the key). From the W3 specs:
The actual function performed by the POST method is determined by the
server and is usually dependent on the Request-URI.
The action performed by the POST method might not result in a resource
that can be identified by a URI.
Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields.
If your data is simple, this SO link might prove a bit useful.

Don't execute a $resource request in angular when there is already a request running

I use a interval of 10 seconds for sending a request to get the most recent data:
var pollInterval = 10000;
var poll;
poll= $interval(function()
{
getNewestData();//$resource factory to get server data
}, pollInterval );
This works fine voor 99% of the time, but if the internet speed is really slow(I have actually experienced this), It will send the next request before the current is finished. Is there a way to just skip the current interval request if the previous one is still busy? Obsiously I could just use booleans to keep the state of the request, but I wonder if there is a better(native to angular) way of doing this?
Use the $resolved property of the Resource object to check if the previous operation is done.
From the Docs:
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
$resolved: true after first server interaction is completed (either with success or rejection), false before that. Knowing if the Resource has been resolved is useful in data-binding.
$cancelRequest: If there is a cancellable, pending request related to the instance or collection, calling this method will abort the request.
-- AngularJS ngResource $resource API Reference.
How about making the request, then waiting for that to complete and then wait 10 seconds before making the same request again? Something along this line:
var pollInterval = 10000;
var getNewestData = function () {
// returns data in promise using $http, $resource or some other way
};
var getNewestDataContinuously = function () {
getNewestData().then(function (data) {
// do something with the data
$timeout(function () {
getNewestDataContinuously();
}, pollInterval);
});
};
getNewestData is the function that actually makes the request and returns the data in a promise.
And once data is fetched, a $timeout is started with timer as 10 seconds which then repeats the process.

Abort JSONP request AngularJs

I have the following code:
var canceler = $q.defer();
$http.jsonp(urlWithParams, {
timeout : canceler.promise
}).error(function(data, status, headers, config) {
...
}).success(function(data) {
...
}
});
canceler.resolve();
The error handler of the request gets executed however in the network log of both Firefox and Chrome give 200 responses and return the JSON response. So while the application behaves like the request was aborted, in reality it wasn't? When canceling the request I would have expected the request to have been aborted than returning a 200 success.
You're not doing anything wrong.
Generally, because http is stateless, you can't abort a request once it's been sent to the server. You can, however, stop waiting for and ultimately ignore an eventual response - as you are doing here.
Unless you feel that the error handler shouldn't have been fired because the response succeeded? You don't say whether you're concerned that it was incorrectly failed, or that the aborted request received a response.
The final statement in your code canceler.resolve() will trigger the error. From the angularjs docs:
timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
So I think if you don't call canceler.resolve() it won't call the error function.

Is it suitable to use $httpBackend in production to abstract data service requests?

I have a data service in my application that is responsible for retrieving information for my controllers. This information might come from local storage, window or an ajax request. The problem I am facing is the $q promise responses don't look like $http responses.
this.getContactDetails = function(data) {
// The first time this method is called, we expect contact details to be preloaded on the page.
// We want to read and return that object then remove it from the page so subsequent requests are to the server.
if(typeof $window.preloadData.contact !== 'undefined') {
var contactDetails = JSON.parse(JSON.stringify($window.preloadData.contact));
delete $window.preloadData.contact;
// Since the method call should always have the same return type, we manually create a deferred object and set the resolution using the $q service.
var deferred = $q.defer();
deferred.resolve(contactDetails);
return deferred.promise;
}
var request = requests.contactDetails.get;
return $http(request);
};
The $q service does a nice job here but it resolves as the object it was given. I wouldn't really expect it to wrap the response. I know $httpBackend could accomplish this.
$httpBackend.whenGET(request).respond(contactDetails);
But the service is used in the MockE2E library and I doubt this was its intended use. I am not sure how to call this off afterwards or what would happen if I used it twice on the same request but I can figure out these questions. My other concern is that there doesn't seem to be a way to pass the same config object to $httpBackend as I do to $http. $httpBackend only accepts a method, url, body and headers, while $http config allows me to specify parameters.
Currently my work-around is simply to create and $http-like wrapper myself.
var contactDetails = JSON.parse(JSON.stringify({
data: $window.preloadData.contact
}));
But I don't find this very elegant. Is there a better/correct way to do this?
You can implement your storage layer as a $cacheFactory and add it to $httpProvider during the configuration phase.
From the docs:
When the cache is enabled, $http stores the response from the server in the specified cache. The next time the same request is made, the response is served from the cache without sending a request to the server.
Hence, if you provide your own implementation of a cache with the following methods:
{object} info() — Returns id, size, and options of cache.
{{*}} put({string} key, {*} value) — Puts a new key-value pair into the cache and returns it.
{{*}} get({string} key) — Returns cached value for key or undefined for cache miss.
{void} remove({string} key) — Removes a key-value pair from the cache.
{void} removeAll() — Removes all cached values.
{void} destroy() — Removes references to this cache from $cacheFactory.
You can return values read from localStorage, session cookies, etc. and they will be treated as there were data sent from the server, just without the AJAX request.

Resources