Angular Promise not working - javascript

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.

Related

$http results not retrieving from cache?

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.

Angular javascript data that is nested objects I cannot seem to get d.data.Array[2] to work

I had been using a angular service in which the data was returning just fine.
However, I wanted to instead call directly to the json file except now it doesn't like the data.
Working version
Controller code:
var confirm = this;
confirm.booking = airConfirmationService.getTestData();
Service code :
.factory('airConfirmationService', airConfirmationService);
var confirm = {};
confirmed.getTestData = function () {
return {
"flightData": [
{
"MultiCarrier": false,
// etc...
However I am switched to a service with .service and calling the location of the .json file up directly.
Not working ( well, it pulls the data but returns in a way that I don't understand)
.service('airConfirmationService', function ($http) {
this.getTestData = function () {
return $http({
url: '../apps/temp/Api_Responses/confirm.booking.json',
method: "GET"
})
}
});
Then in controller
var confirm = this;
confirm.booking = airConfirmationService.getTestData();
console.log(confirm.booking) // Picture attached shows how the data looks
// My attempt at getting "at" the data ...
//var temp = [];
//temp = airConfirmationService.getTestData();
//confirm.booking = temp.d.Data;
UPDATE
While this code below "works" I have a feeling that not doing "q" /defer / .then will be bad...
This code works in the controller calling the service, but how can i add/change to have q/defer and/or .then ?
var getData = airConfirmationService.getTestData();
getData.success(function(data) {
confirm.booking = data;
});
Angular $http is Asynchronous. The network operation to get the data from the server may take some time, so angular does not expect your app to wait until the operation is complete, stuck hung until the data is available. Therefore, a promise object is used as a placeholder for the data. Promises have a .then() function which tells the promise that it should execute some other code after the operation is complete. They also provide a .catch() function for when something goes wrong and the data isn't returned.
The correct way to use $http is:
var confirm = this;
confirm.testData = {};
airConfirmationService.getTestData()
.then(function(response) {
confirm.testData = response.data;
})
.catch(function(){
//something went wrong
});

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.

manage factory JSON $http data with 2 controllers

I'm trying to get a factory JSON response, save it in a variable, in order to be ready to be called from 2 different controllers.
Here bellow I paste the code I'm using:
storyFactory.js
var story = angular.module('story.services', []);
story.factory('storyAudio', [ '$http', function ($http) {
var json = {};
function getJSON(story_id, callback) {
$http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
}).success(function (data) {
json = data;
callback(data);
});
};
return {
getSubaudios: function(story_id, callback) {
getJSON(story_id, function(result) {
callback(result);
});
},
getTopbar: function(callback) {
callback(json);
}
};
}]);
StoryCtrl.js
var storyCtrl = angular.module('story', ['story.services']);
storyCtrl.controller('storyCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data = this;
data.story = {};
storyAudio.getSubvideos($stateParams.story_id, function(response) {
data.story = response;
});
}]);
TopbarCtrl.js
var topbarCtrl = angular.module('topbar', ['story.services']);
topbarCtrl.controller('topbarCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data2 = this;
data2.story = {};
storyAudio.getTopbar(function(response) {
data2.story = response;
});
}]);
The problem is in my TopbarCtrl response I'm receiving an empty data2.story when I call it in the HTML.
The reason is because it doesn't have a callback of the $http response, so it prints the var json with the actual status, that is an empty object.
How could I load the second controller when the variable has content?
Thanks in advice.
I think the best you can do in this case is load the data via getSubaudios and provide a reference to the data for other controllers to use. Something like this...
story.factory('storyAudio', function($http) {
var factory = {
story: {}
};
factory.getSubaudios = function(story_id) {
return $http.get('https://api.domain.co/get/' + story_id).then(function(response) {
return angular.extend(factory.story, response.data);
});
};
return factory;
})
Using angular.extend() instead of directly assigning a value to the factory's story property maintains any references that may be established before the data is loaded.
Then you can load the data via
storyCtrl.controller('storyCtrl', function(storyAudio) {
var data = this;
storyAudio.getSubaudios($stateParams.story_id).then(function(story) {
data.story = story;
});
})
and directly reference the story data by reference in your controller
topbarCtrl.controller('topbarCtrl', function(storyAudio) {
this.story = storyAudio.story;
})
I think I'm understanding correctly, but let me know if not.
There are two issues I'm seeing. The first is that there is a typo in your StoryCtrl.js file. You are calling "storyAudio.getSubvideos" but the function is called "getSubaudios" in your factory.
Even with that typo fixed, the issue could still technically happen. It all really depends on how quickly the promise returns from the first call. Unfortunately, promises are asynchronous, so there is no guarantee that the "json" variable will get set before the second controller tries to get it.
In order to resolve this, you need to ensure that the first call is finished before trying to access the "json" variable you have on the service. There are probably a few different ways to do this, but one that comes to mind is to actually return and store the promise in the service like so...
var dataPromise;
function getSubaudios(story_id){
if(!dataPromise){
dataPromise = $http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
});
}
return dataPromise;
}
return {
getSubaudios: getSubAudios
};
Then in your controllers, you can just call the service and use .then to get the data out of the promise when it returns...
storyAudio.getSubaudios($stateParams.story_id).then(function(response){
data.story = response; //or data2.story = response;
});
Here is a plunkr example. I've used the $q library to simulate a promise being returned from an $http request, but it should illustrate the idea.
Similar to Phil's answer. (Angular extend, or angular copy keeps the references the same in both controllers. If you don't want to put watchers in both controllers to keep track if the value changes.) Several methods here:
Share data between AngularJS controllers.
You could also bind the object you are returning directly to the update-function. That way the references stay intact.
storyServices.factory('storyAudio', ['$http', function($http) {
return {
data: { json: '' },
getSubaudios: function(story_id) {
$http.get('http://jsonplaceholder.typicode.com/posts/' + story_id)
.then(function(response) {
this.data.json = response.data.body;
}.bind(this));
}
};
}]);
var storyCtrl = angular.module('story').controller('storyCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data = storyAudio.data;
storyAudio.getSubaudios(2);
}]);
var topbarCtrl = angular.module('story').controller('topbarCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data2 = storyAudio.data;
}]);
Plunk here: http://plnkr.co/edit/auTd6bmPBRCVwI3IwKQJ?p=preview
I added some scopes to show what happens.
Sidenote:
I think it's straight out dishonest to name your non-controller "storyCtrl" and then assign it a controller of its own:
var storyCtrl = angular.module(...); // Nooo, this is not a controller.
storyCtrl.controller(...); // This is a controller! Aaaah!
Another sidenote:
.success() is the old way of doing things. Change to .then(successCallback) today! I dare to say it's the standard convention for promises.
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice

Promise always returns the initial value

having a bit of a problem with promises in angularjs.
My promises 'get cached', meaning they always return the initial value they got called with. I'm pretty familiar with promises from other places, but new to angularJS, so please help me shed a light on my problem, I'm probably not understanding something very basic here
I am using a factory:
.factory('Traffic', function ($http) {
var trafficUrl = 'some url';
var httpPromise = $http.get(trafficUrl, {cache: false} );
var invalidateCache = function() {
return $http.get(trafficUrl, {cache: false} );
}
return {
all: function () {
httpPromise = invalidateCache();
return httpPromise
.then(function (response) {
//parsing the response and returning stuff (not promise)
}
}
})
which is sending a request, and parsing it for the first time.
now invalidateCache was suggested by someone to avoid exactly my problem (assign a new $http.get each time to avoid it referring to the same, initial promise).
now my controller:
.controller('TrafficCtrl', function ($interval, $ionicLoading, $scope, Traffic) {
var getJams = function () {
var traffic = Traffic.all();
traffic.then(function (response) {
//doing stuff with the response
})
};
$scope.refresh = function () {
getJams();
}
})
now, each time I invoke $scope.refresh method, I see the items getting 'refreshed' (console.log gets called inside getJams) but the values stay as the first called getJams().
Thanks.
From your comment, it sounds like the browser is caching your response, so you will want to update the server logic to set Cache specific headers.
You will probably want to add the following Cache-Control header to your response:
Cache-Control: no-store, no-cache
A little more info about the cache headers here.
Should be able to find plenty of examples to set this in your server side language of choice.
Also, you can clean up the code much more:
.factory('Traffic', function ($http) {
var trafficUrl = 'some url';
return {
all: function () {
// don't need the invalidate call anymore
return $http.get(trafficUrl).then(function (response) {
//parsing the response and returning stuff (not promise)
}
}
})
And your controller:
var getJams = function () {
// No need to store the initial promise call, just chain them.
Traffic.all().then(function (response) {
//doing stuff with the response
})
};

Categories

Resources