How can I access the $scope or the data obtained from success in $http outside the $http.jsonp() request?
$http.jsonp('http://example.com/?callback=JSON_CALLBACK')
.success(function(data) {
$scope.info1 = data.name;
$scope.info2 = data.company;
});
console.log("access it here outside: ",$scope.info1);
currently the console prints undefined.
Thanks for the help.
You shouldn't consider asynchronous ajax call to be work in synchronous way. You have to wait until that ajax/promise gets finished. Though don't use .success/.error they are deprecated, use .then instead to chain promise.
You must rely on the promise to promise gets resolve/reject.
Code
var promise = $http.jsonp('http://example.com/?callback=JSON_CALLBACK')
promise.then(function(response) {
var data = response.data;
$scope.info1 = data.name;
$scope.info2 = data.company;
console.log("access it here outside: ",$scope.info1);
myOtherFunction($scope.info1);
})
.catch(function(error) {
console.log(error);
});
Related
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.
I am new to angularjs. I am using Angular 1.5
I want to load data from server and store it to scope and use it in view file.
My data is stored in the scope after load the view file. How can I store data to scope first and than load the view file so that I can use scope data in view file.
Here is my code
$scope.getProfile = function() {
Account.getProfile()
.then(function(response) {
$scope.user = response.data;
console.log($scope.user); //here data printed in console
})
.catch(function(response) {
console.log(response.data.message, response.status);
});
};
if($auth.isAuthenticated()){
$scope.getProfile();
console.log($scope.user)//here data not print in console.
}
Code in .then blocks execute asynchronously after the function returns.
Have the function return a promise for the value and extract that value in a .then block:
$scope.getProfile = function() {
return Account.getProfile()
.then(function(response) {
$scope.user = response.data;
console.log($scope.user); //here data printed in console
return $scope.user;
})
.catch(function(response) {
console.log(response.data.message, response.status);
throw response;
});
};
if($auth.isAuthenticated()){
var promise = $scope.getProfile();
promise.then(function(user) {
console.log(user);
});
};
By using a promise returned by the function, a .then block can be created that will execute after the data has returned from the server.
Explaination of Promise-Based Asynchronous Operations
console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
console.log("Part3");
});
console.log("Part4");
The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.
For more information, see How to use $http promise response outside success handler.
Demo
console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
console.log("Part 3");
});
console.log("Part *4*");
It's because of the Async behavior, you might need to use the callbacks. Give this a try
$scope.getProfile = function(callback) { //added callback function
Account.getProfile()
.then(function(response) {
$scope.user = response.data;
callback($scope.user); //callback execution
})
.catch(function(response) {
console.log(response.data.message, response.status);
});
};
And your caller should be like following:
if ($auth.isAuthenticated()) {
$scope.getProfile(function(user){
$scope.user = user;
});
}
Hope this will help
If you want to execute some code every time an angular page loads, you could make use of ‘resolve’, as long as ‘UI-Router’ setup.
If you just want to execute some code without loading data, you are better off using ‘$viewContentLoading‘ or ‘$viewContentLoaded‘. Please note the following documentation.
You can refer here for more information
change your code as shown below:
$scope.getProfile = function() {
Account.getProfile()
.then(function(response) {
$scope.user = response.data;
console.log($scope.user); //here data printed in console
if($auth.isAuthenticated()){
console.log($scope.user)//here data not print in console.
}
})
.catch(function(response) {
console.log(response.data.message, response.status);
});
};
$scope.getProfile();
Hope this helps. :-)
I have 2 csv's that I'm trying to read in, after which I use the data in those two to do stuff:
function getData() {
var deferredObject = $.Deferred(); //representation of some asynchronous work
d3.csv("./parse_shp.csv", function(data) {
console.log(data);
shp_array = data;
});
d3.csv("./fao_coutnry_shp.csv", function(data) {
console.log(data);
fao_array = data;
});
//once both of those are done, resolve the promise
deferredObject.resolve();
return deferredObject.promise();
}
function LevenshteinDistance() {
console.log("do stuff with the data");
}
//call LevenDistance after the promise has been resolved
getData().then(LevensteinDistance());
But that's not working... it'll print the line "do something with the data" before printing the data of the csv's.
What am I doing wrong? I used this link as an example.
I don't understand how connect deferredObject and getData()? Because even if I create the deferred object in the function, won't it just asynchronously do the csv reads anyway and then erroneously call defferedObject.resolve()?
Anyway, I'm new to promises so any help would be greatly appreciated!!
The argument to .then() must be a function. You're calling the function immediately, because you have () after the function name. It should be:
getData().then(LevenshteinDistance);
It doesn't seem like I can return a deferred object if it failed. I have a pretty nested ajax request that goes into a queue so I need a way to return the request as a deferred object. Everything is great if the request is successful, but I am stuck on getting failures to propagate.
https://jsfiddle.net/3wtady9r/
function doAjax(file) {
var defer = $.Deferred();
var fakeFile = file;
var data = null;
ajax();
function ajax() {
return $.get(fakeFile)
.done(function(data) {
data = data;
defer.resolve(data);
})
.fail(function() {
defer.fail();
})
}
return defer.promise(data);
}
var thisWillWork = '';
doAjax(thisWillWork)
.done(function() {
console.log('done')
})
.fail(function() {
console.error('fail')
})
.always(function() {
console.log('always')
})
var thisWontWork = 'fakeFile.html'
doAjax(thisWontWork)
.done(function() {
console.log('done')
})
.fail(function() {
console.error('fail')
})
.always(function() {
console.log('always')
})
When I do doAjax(thisWontWork) I am expecting to get the console error and also .always, but neither fire.
There are a bunch of different issues there.
fail is a method for hooking up handlers to get called if the promise is rejected. To reject it, use reject.
You don't want to pass data into defer.promise at the end.
You don't want or need a data variable at all, just the argument you receive in success.
Since you never use the value you return from your own ajax function, there's no need to return it.
jQuery's ajax already returns a promise that does what your promise does, so your promise can just be removed entirely. This happens often enough there's an antipattern named for it. Every time you think you need to create a promise, stop yourself and think: Do I already have one? Sure, sometimes the answer is no, but often it's yes. :-)
As of point #5, we end up with:
function doAjax(file) {
var fakeFile = file;
return $.get(fakeFile);
}
...which means, of course, that you don't need doAjax at all. Just call $.get.
But if for some reason you haven't shared you need your own promise, then just points 1-4:
function doAjax(file) {
var defer = $.Deferred();
var fakeFile = file;
ajax();
function ajax() {
$.get(fakeFile)
.done(function(data) {
defer.resolve(data);
})
.fail(function() {
defer.reject();
});
}
return defer.promise();
}
It should be defer.reject(); not defer.fail();
Also you can add data in the reject like you did in defer.resolve().
I'm using angularjs' $http method to get multiple "parent" elements. In this Ajax Calls .success method, I have to iterate over the parent elements, and use yet another Ajax call for every parent element, to get its respective child elements. What I want in the end, is an Array containing all the child element objects, so I can display them using ng-repeat. That's why I want to collect all the child elements in an array first, and the write that array to the scope array I'm using to display, so angular will only update when all child elements are collected. I'm not that versed in using promises, but I think this should be possible by using them. The structure is basically:
.success(function(parentElements){
var tempChildElements = [];
$.each(parentElements, function(i, parentElement){
getChildElements(parentElement)
.success(function(childElements){
tempChildElements.concat(childElements);
})
})
});
$scope.childElements = tempChildElements;
});
Basically, I need to know when all the requests inside jQuery.each are finished.
EDIT:
So, I changed my code to incorporate your answers, and I think I'm close but it's still not working. What I got is:
$scope.loadChildren = function(){
var tempchildren = [];
var promises = [];
restApi.getOwnparents() //Returns $http.get promise
.then(function(parents){
parents.data.forEach(function(parent, i, parents){
promises.push(restApi.getOwnchildren(parent) //Returns $http.get promise
.then(function(children){
tempchildren = tempchildren.concat(children.data);
},function(msg){
console.log(msg);
}));
});
}, function(msg){
console.log(msg);
});
$q.all(promises).then(function(data){
//Never gets called
$scope.currentElements = tempchildren;
console.log(tempchildren);
});
};
EDIT 2:
I got it to work using suggestions from you guys, below is my code. Feel free to share improvements.
$scope.loadparents = function(){
var tempchildren = [];
var promises = [];
restApi.getOwnparents()
.then(function(parents){
parent.data.forEach(function(parent, i, parents){
promises.push(restApi.getOwnchildren(parent));
});
$q.all(promises).then(function(data){
console.log(data);
data.forEach(function(children){
tempchildren = tempchildren.concat(children.data);
});
$scope.currentElements = tempchildren;
});
});
};
Something like this might be a possibiliy. Loop through your parentElements calling getChildElements with that element. However the response from getChildElements will be a promise if your returning the $http call so push that into an array and pass that array to $q.all. When all your ajax calls resolve so will $q.all.
var parentElements = [10, 20, 30, 40],
promises = [];
parentElements.forEach(function(i){
//Each method is actually called here
promises.push(getChildElements(i));
});
//$q.all will resolve once all of your promises have been resolved.
$q.all(promises)
.then(function (data){
//handle success
console.log('All Good', data);
//Modify your response into what ever structure you need, map may be helpfull
$scope.childElements = data.map();
});
Most likely your ajax call won't be resolved by the time the array is passed to $q.all however another nice thing about promises is even if they are all resolved $q.all will resolve straight away instead.
See it in action. http://jsfiddle.net/ht9wphg8/
Each request itself returns a promise, which can then be put into an array and pass that array to $q.all().
The success() callback is deprecated and since you need to return promises you need to use then() anyway in your original request callback.
Here's a sample factory that would make all the requests and once done you would have the parent data of first request returned to controller:
app.factory('DataService', function($http, $q) {
return {
getData: function() {
// return initial promise
return $http.get('parent.json').then(function(resp) {
var parentArr = resp.data;
// create array of promises for child data
var promises = parentArr.map(function(parentItem, i) {
// return each child request promise to the array
return $http.get('child.json').then(function(resp) {
console.log('Child request #' + (i + 1) + ' completed');
// update parent item
parentItem.child = resp.data
});
});
// return the promise wrapping array of child promises
return $q.all(promises).then(function() {
console.log('All requests completed');
// when all done we want the parent array returned
return parentArr;
});
});
}
};
});
app.controller('MainCtrl', function($scope, DataService) {
DataService.getData().then(function(parentArr) {
console.log('Add data to scope')
$scope.parentArr = parentArr;
});
});
DEMO