Can I create an $http request and not submitting it (yet)? - angularjs

I have a generic component in my system that deals with submitting HTTP requests in chunks. It takes care of some application-common headers that I need to attach and updating all the necessary places in the GUI.
This component is shared between couple other components for various use-cases. I'm trying to delegate the task of creating HTTP request from a given input to the callers but found out that when the caller does:
var req = $http.get("url", {})
It's already submitting the request.
I'd like the callers to provide a method to generate a list of request objects from the input, and my component will deal with that later (add some headers for example, add success() and error() methods, or submit the requests in batches).
How can I only create the HTTP Request object, without sending it?

You can create wrapper function for "delayed" http request, that would return you preconfigured ready to call function, and use it instead of $http.
Maybe like this:
function dHttp(config) {
return function(options) {
angular.merge(config, options);
return $http(config);
}
}
var req = dHttp({
url: '/some',
data: {test: 21}
});
// later send it
req().then(function(data) {
console.log(data);
});

A plain AJAX can be useful here
xmlhttp.open("GET","a-url",true);
xmlhttp.send();
$http.get() is a declarative form of an ajax open and send.
var reqArr = [];
reqArr.push(xmlhttp.open("GET","a-url",true));
reqArr.push(xmlhttp.open("GET","b-url",true));
reqArr.push(xmlhttp.open("GET","c-url",true));
so you can change the objects as needed and send the ajax later on.

Related

$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.

How to 'add' cookie only for angular.http request

I need to emulate some state in my developer utility, and for it I need to pass some special cookie to the http request which made via angular $http module.
Code for making http request looks simple:
if (condition){
var headers = getHeaders();
if (headers !== 'undefined'){
config['headers'] = headers;
}
}
return $http(config);
So looks like I should add some field to the this config. But I can't find any related data about this in $http docs.
I do not want to set cookie 'to the browser' becase I want to pass it only for some several requests performed asynchronously.
Is it real to nmake it with angular.js?

How can I use my JSON without triggering a new API request everytime I want to use my data?

I'm wanting to to have my app limit its requests to the server without saving the data. Is there a way I can do this without making a request to the server every time my http request is invoked? What are some of the different methods this can be done and best practices/trade offs for each?
That would depend on you needs. One approach would be to store the results in a request in a factory and retrieve those.
app.factory('DataService', function($http) {
var values;
var requestValues = function() {
$http.get("/api/getValues").then(
function(results){
values = results;
});
};
var getValues = function() {
return values;
};
return {
requestValues : requestValues, // this will make a http request and store the result
getValues: getValues // this will call the stored result (without making a http request)
}
});
And then in you controller call the functions to make a request for the values and then get the values. There are two functions, requestValues() to make the http request and save the result and getValues() to get the stored values without making a http request. Once requestValues() has been called, you should be able to call getValues() from anywhere to get the values without making a new http request.
myApp.controller('MyController', function ($scope, DataService) {
var init = function (){
DataService.requestValues(); // this will make the http request and store the result
$scope.items = DataService.getValues(); // this will get the result
};
var justGetValues = function(){
$scope.items = DataService.getValues(); // this will get the result (without making a http request)
};
});
Now you simply have to call DataService.getUpdates(); whenever you need the values. (You might want to wrap these in a promise. I have refrained from doing this due to simplicity)
Alternatively you could use the cache option as mentioned by #JLRishe. Angular's $http has a cache built in, so simply set cache to true in its options
$http.get(url, { cache: true})
.success(){
// on success
}.error(){
// on error
};
You can use the cache option in the config argument to the various $http methods:
$http.get('/someUrl', {
cache: true
});
The most obvious way is simply to have a JavaScript integer variable that counts how many requests have been made to the server, and when a defined maximum limit is reached, to stop making requests to the server.
Without knowing more about why you want to do it, it is difficult to be more specific.

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.

AngularJS unit testing: $httpBackend.when dynamic responses

I'm playing with AngularJS unit testing when developing Feed (RSS) application. The Feed can fetch remote rss data parse it and save parsed items. For testing rss fetching I use $httpBackend mock:
beforeEach(inject(function (_$httpBackend_, _feedMock_, _mockConfig_) {
_$httpBackend_.when('GET', _mockConfig_.FEED_URL).respond(_feedMock_);
}));
and then below
$httpBackend.expectGET(mockConfig.FEED_URL);
feed.fetch();
$httpBackend.flush();
it works fine.
But I need to make the Feed can actualize it's state by fetching updated rss data and appending new items. So, the Feed make the same request, but got new updated data. I'm trying to recreate server definition by something like this:
$httpBackend.when('GET', _mockConfig_.FEED_URL).respond(feedMockUpdated);
and then make the same operation with expect and flush, but $httpBackend response with old data (feedMock), not new (feedMockUpdated). How can I make $httpBackend to response with the different data on the same request?
You can set up respond with a function, not just a static data set.
From the docs:
http://docs.angularjs.org/api/ngMock.$httpBackend
respond – {function([status,] data[, headers])|function(function(method, url, data, headers)} – The respond method takes a set of static data to be returned or a function that can return an array containing response status (number), response data (string) and response headers (Object).
So as I understand in your case, you want to have the same endpoint return different data on later calls, you can try something like the following. This is just implementing a simple counter that will switch the data on a subsequent call.
var rssCalled = 0;
$httpBackend.when('GET', _mockConfig_.FEED_URL).respond(function(method, url, data, headers){
if(rssCalled === 0) {
rssCalled++;
return [200, feedMock, {}];
} else {
return [200, feedMockUpdated, {}];
}
});

Resources