$http results not retrieving from cache? - javascript

Hey on first $http requests I save my results in cache i am using two fucnctions in my controller calling the same function in service where is calling the $Http at first function for $Http it save the results in cache but for the second function when i tried to test it out wehter cache is empty or not it should not be empty as i already save results in my cache but it gives me
undefined error
Can Anyone tell what's going on wrong on my code
Here is the controller
vm.getTopHotels = function(){
var hotelsLimit = 10;
var top_hotels =
dataService.getHotels()
.then(
function(hotels){
console.log(hotels);
sortHotels = commonMethods.sortHotels(hotels.data.data,'Rating','SORT_DESC');
hotelDetailsCheck = checkDetailsIfExists(sortHotels);
//Get only top 10 hotels for home page
top_hotels = hotelDetailsCheck.slice(0,10);
vm.topHotels = top_hotels;
},
function(err){
console.log(err);
});
};
vm.getTopHotels();
vm.getRHotels = function(){
dataService.getHotels()
.then(function(hotels){
console.log('hotels recieced 2 ');
},
function(err){
console.log(err);
});
}
vm.getRHotels();
**dataService is Facotry here that is calling the $http methods **
for the vm.getTopHotels I'm saving results in the cache so getRHotels when call the $Http i am chcecking that if the cache is not empty it should retreive the data from the cache if not then it call the $Http request but for this function too it is calling the $http why? because i have already save the results in cache Can anybody tell me what is wrong?
Here is the dataService Code which is calling the $http methods and saving in Cache
(function(){
angular
.module('app')
.factory('dataService',DataFactory);
DataFactory.$inject = ['$http','$q','$cacheFactory']
function DataFactory($http,$q,$cacheFactory){
var cache = $cacheFactory('localCache');
var service = {
getHotels:getHotels
};
return service;
function getHotels(){
var def = $q.defer();
var hotelsData = cache.get('hotelsData');
console.log(hotelsData);
if(!hotelsData){
$http.get('/hotels/getHotelsData')
.then(
function successCallback(data){
cache.put('hotelsData',data.data);
// service.hotels = data.data;
def.resolve(data);
},
function errorCallback(data){
def.reject('Failed to retrieve hotels');
});
return def.promise;
}else{
console.log('cached');
}
}
}
})();

You can actually specify $http to cache results by adding cache:true to your config object.
function getHotels() {
return $http.get('/hotels/getHotelsData', {cache:true});
}
You can read more about the $http config here:https://docs.angularjs.org/api/ng/service/$http
Also to clarify, $q.defer is a helper that alows you to wrap non promise API callbacks as promises. $http returns a promise. You can just return the response of $http.get and perform a .then on it.
If you need to manipulate the data before returning it, you still don't need to wrap it within $q.defer()
function getHotels() {
return $http.get('/hotels/getHotelsData', {cache:true})
.then(function(response){
response.data[0].hotelName = 'changedName';
return response;
})
}

The getHotels function has a race condition: A second call to the function before the data returns from the server will allow a second HTTP GET request.
Since the $http service immediately returns a promise, it is better to cache that promise.
var hotelsPromise = null;
function getHotels(){
if (hotelsPromise) return hotelsPromise;
//ELSE
hotelsPromise = $http.get('/hotels/getHotelsData')
.then(function successCallback(response){
return response.data;
},
function errorCallback(response){
throw 'Failed to retrieve hotels';
});
return hotelsPromise;
}
This will avoid erroneous multiple HTTP GET requests.

Related

How to Handle multiple Ajax call calling same function?

Hi have 4 directives on the page, all directive require list of user.
User List is stored in database which require hhtp request.
As 4 directive are on the page at same time so 4 different ajax call has been sent to server for similar response.
And then the response is getting cached.
What can be done that all 4 directive recieves its user list and only one ajax is sended to server.
Code Inside Directive(self is this) (ajaxCallService is service)
ajaxCallService.getUser(function(response) {
self.users = response;
//Next operations
});
ajaxCallService Service
Variable
var userList = []
Method
if (!userList.length) {
$http.post({
url: #,
data:#
})
.then(
function(response) {
userList = response.allMembers;
callback && callback(userList);
}
);
}else{
callback && callback(userList);
}
How can i prevent 4 ajax calls and only make 1 call and let other 3 wait for the response and pass response back?
You can use promises for this, to see if it is already running. Because services are singletons in angular, you know it is always a shared instance:
var userList = [];
var promise;
function getUser() {
// inject the $q service
var deferred = $q.defer();
if (userList.length) {
// already got data, immediately resolve that one
deferred.resolve(userList);
}
if (promise) {
// a promise is already working on it, return this one
return promise;
} else {
$http.post(...).success(function(response) {
userList = response;
deferred.resolve(response);
});
}
// if this point is reached, this is the first call
// assign the promise so other calls can know this one is working on it
promise = deferred.promise;
return promise;
}

How angular interceptors works?

I was learning angular interceptors today. I did some samples to to better understand the concept. Here is small sample.
var app = angular.module("myApp", []);
app.factory("timestampMaker", [
function() {
var timestampMaker = {
request: function(config) {
console.log(config);
config.requestTimestamp = new Date().getTime();
return config;
},
response: function(response) {
response.config.responseTimestamp = new Date().getTime();
return response;
}
};
return timestampMaker;
}
]);
app.config(['$httpProvider',
function($httpProvider) {
$httpProvider.interceptors.push('timestampMaker');
}
]);
app.run(function($http) {
$http.get('https://api.github.com/users/naorye/repos').then(function(response) {
console.log(response);
var time = response.config.responseTimestamp - response.config.requestTimestamp;
console.log("The request took" + (time / 1000) + "seconds")
});
});
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
</html>
When I am doing console.log(config) inside request function, here is the output on the console.
I am not getting how responseTimestamp appear in the config object of request where as its defined inside response function
Note I used the source code for 1.2.x which is the one added in the question.
$httpProvider is a provider that gives you $http service.
As a provider, in config time exposes a public array - there you just add all strings from services names you wanted to be injected in your response/requests.
That is what you do here
app.config(['$httpProvider',
function($httpProvider) {
$httpProvider.interceptors.push('timestampMaker');
}
]);
and it can be only done in config time because per docs providers can be configured before the application starts
You can see the exposed array in the source code in line 159
var responseInterceptorFactories = this.responseInterceptors = [];
When you request the $http service, injecting it into your service/controller,
$get function is executed. In that function, your array of interceptors is iterated, as you can see in source code in line 179
var reversedInterceptors = [];
forEach(responseInterceptorFactories, function(interceptorFactory, index) {
var responseFn = isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory);
/**
* Response interceptors go before "around" interceptors (no real reason, just
* had to pick one.) But they are already reversed, so we can't use unshift, hence
* the splice.
*/
reversedInterceptors.splice(index, 0, {
response: function(response) {
return responseFn($q.when(response));
},
responseError: function(response) {
return responseFn($q.reject(response));
}
});
});
Per convention, they reverse the order. You can see that using the string, they get a reference to the function using $injector.get or $injector.invoke, and using $q.when() we can introduce them to the promises chain, even if they are synchronous code - See this Can I use $q.all in AngularJS with a function that does not return a .promise? if you are not sure what I meant about $q.when()
So far we have an array with functions, which are all promise-like (thanks to $q.when()). When you request data through $http like this
$http.get('https://api.github.com/users/naorye/repos').then(function(response) {
console.log(response);
var time = response.config.responseTimestamp - response.config.requestTimestamp;
console.log("The request took" + (time / 1000) + "seconds")
});
even though you have the .get(), is just a shortcut for all the same functionality which is here
In the code the relevant part is this one:
First, a chain array is created with two values: an inner function which is not important for our purpose (but it returns a promise - this is important), and undefined value.
Our array with interceptors is iterated, and request interceptors are added at the beginning (before the request) and response at the end. Like this
function serverRequest {
// some code
};
var chain = [serverRequest, undefined];
var promise = $q.when(config);
// apply interceptors
forEach(reversedInterceptors, function(interceptor) {
if (interceptor.request || interceptor.requestError) {
chain.unshift(interceptor.request, interceptor.requestError);
}
if (interceptor.response || interceptor.responseError) {
chain.push(interceptor.response, interceptor.responseError);
}
});
then, having the chain complete (remember our array of interceptors was full of promises), the code iterates it, adding all of them using .then(), causing all of them to be executed in a chain, following promises chaining (you can google that)
while(chain.length) {
var thenFn = chain.shift();
var rejectFn = chain.shift();
promise = promise.then(thenFn, rejectFn);
}
Finally, the callback you add in success and error is added at the very end of the chain and the promise is returned
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
return promise;
The only part I am not sure about is the use of undefined in the chain array
Summary
You add the name for your services, $HttpProvider uses $invoke service to get them and adds them in the promise chain using $q.when(), returning a promise. At the end of that, your callbacks for a specific $http request is added.

Manipulate data from promise result

var prom = $http.get('url');
prom.success(function(data){
//here calling some service and updating the data
});
$scope.abc = function(){
//doing some calculation with updated data
}
Once i get data from http request, i am calling some service which adds some info to received data. i.e data+service()=updatedData.
Now,on this updated data I am applying some calculation, and displaying it in view.
But this thing is not working. I tried to add this calculation to prom.success itself, but still I am not getting updated data. I tried setInterval() to $scope.abc but some time it display sometime won't.
Please help me to solve this problem.
Thank you
Write a method in a service that will resolve the data. In the method first call $http to get the data. After it has got the data, call the 2nd service which will add extra info. Once that is done (i.e. the promise for the 2nd service has been resolved), resolve the update data. From the controller call this method in the service, and wait for it to be resoved. When it's resoved; 'then' call your method on scope that will display the data. I will post a working fiddle for you.
You can check the fiddle here.
Service
myApp.factory('service', function($http, $q) {
var addInfoToData = function(data) {
var deferred = $q.defer();
data.updatedInfo = "Some dummy info";
deferred.resolve(data);
return deferred.promise;
};
//Dummy Method for $http call
var callApi = function() {
var deferred = $q.defer();
var dummyData = {
Id: 1,
Name: "Fake Info",
Value: 15
};
deferred.resolve(dummyData);
return deferred.promise;
}
var getData = function() {
var deferred = $q.defer();
//Replace with $http
callApi().then(function(data) {
addInfoToData(data).then(function(updatedData) {
deferred.resolve(data);
})
})
return deferred.promise;
}
return {
getData: getData
}
})
Note: callApi method is a dummy method for $http call. Replace it with your actual API call.
Controller
function MyCtrl($scope, service) {
$scope.name = 'Superhero';
$scope.abc = function(data) {
data.Value = data.Value / 100;
$scope.displayData = data;
}
var getData = function() {
service.getData().then(function(data) {
$scope.abc(data);
})
}
getData();
}

Angular Promise not working

I try to get some important things like: companyid,employeeid etc. with every request that a user makes. So this has to be received before everything else is done.
After that the user receives information based on his companyid that he sets with every request (get/company/{companyid}).
The problem that I have is that the response for receiving the companyid takes to long and angular already tries to make a request to (get/company/{companyid}) obviously there is no companyid yet.
I've tried to fix this whit promise but it's not working.
Here I try to receive some important information about the user(that I do with every request) :
Service
(function () {
angular.module('employeeApp')
.service('authenticationservice', authenticationservice);
function authenticationservice($http,$location,authenticationFactory,$q,GLOBALS,$cookies) {
this.validateUser = function () {
var vm = this;
vm.deferred = $q.defer();
data = {"api_token": api_token};
return $http.post(GLOBALS.url+'show/employee/' + $cookies.get('employeeid'),data)
.success(function(response)
{
vm.deferred.resolve(response);
})
.error(function(err,response)
{
vm.deferred.reject(err);
});
return vm.deferred.promise;
}
}
})();
Routes file
(In my routes file I use the authenticationservice to set all important users variables.)
employeeAppModule.run([
'authenticationservice',
'constants',
function(authenticationservice,constants) {
authenticationservice.validateUser()
.then(function(response)
{
constants.companyid = response.result.Employee;
constants.role = response.result.Role;
constants.name = response.result.FirstName;
console.log('test');
},
function(response){
console.log('error');
});
}
]);
So the problem is that the user information is set to late and angular already goes to my homeController where he uses the companyId that is not being set yet.
Thankyou
The problem in your current code is return $http.post are having two return statement in your validateUser method. Which is returning $http.get before returning return vm.deferred.promise; & that why customly created promise doesn't get returned from your method. Though by removing first return from $http.get will fix your problem, I'd not suggest to go for such fix, because it is considered as bad pattern to implement.
Rather I'd say, you should utilize promise return by $http method, & use .then to return data to chain promise mechanism.
Code
function authenticationservice($http, $location, authenticationFactory, $q, GLOBALS, $cookies) {
this.validateUser = function() {
var vm = this;
data = {
"api_token": api_token
};
return $http.post(GLOBALS.url + 'show/employee/' + $cookies.get('employeeid'), data)
.then(function(response) {
var data = response.data;
retrun data;
}, function(err) {
return $q.reject(err);
});
}
}
To make sure that $ http return a $ promise object you need to check that the action in the controller returns a value and it is not a void action.

Get value from promise response

from angular contoller I calling http service
curServices = Cust_Serv.get(id);
console.log(curServices);
My service calls like this
get:function(data){
var id= {'id':data};
var promise = $http.post('api/admin/cust_serv/getserv',id).
success(function(response){
}).
error(function(){
console.log('no services found');
});
return promise;
},
I receive response object with $$state, catch, error, finally, success, then, proto.
I need only the values that is inside $$state->value->data.
How to access that data?
I followed as Bergi said and it worked.
Cust_Serv.get(id).then(function(response){
curServices = response.data;
});

Categories

Resources