I'm trying to create an Ionic app and for this I have to make some HTTP GET request inside a for loop but it appears that angular does not wait for the data before showing them.
Here is a code I'm using.
$http.get(ApiUrl.get() + '/Threads' + '?access_token=' + $scope.userToken + filterString + "&filter[order]=created%20desc")
.success(function(data, status, headers, config) {
var i=0;
for(thread in data)
{
$scope.threadObj = data[i];
var threadId = $scope.threadObj.id;
$scope.threadPostNumber;
//On récupére chaque nombre de post
$http.get(ApiUrl.get() + '/Threads/' + threadId + '/posts/count' + '?access_token=' + $scope.userToken)
.success(function(data, status, headers, config) {
$scope.threadPostNumber = data.count;
})
.error(function(data, status, headers, config) {
alert("Connection error, please try again.");
$location.path("/app/carte");
});
$scope.threadObj.count = $scope.threadPostNumber;
$scope.threads[i] = $scope.threadObj;
i++;
}
})
.error(function(data, status, headers, config) {
alert("Connection error, please try again.");
$location.path("/app/carte");
});
The first HTTP get is done and the data can be show within the foreach but when I try to add additionnal data to the original ones with a second get request nothing is created or sometimes only the last value is shown for every one.
The problem stems from the API call being asynchronous, and much slower than the for loop.
The $http.get
posts the request, but it doesn't get the response until long after the for loop completes. As a consequence $scope.threadPostNumber
inside the promise success callback is going to be set after it is assigned in:
$scope.threadObj.count = $scope.threadPostNumber
So effectively this assignment is useless.
In order to fix, use tail recursion or Promise objects
in order to make the calls consecutive.
You can also properly scope the current thread object so that when the promise succeeds you will be modifying the correct thread:
$http.get(ApiUrl.get() + '/Threads' + '?access_token=' + $scope.userToken + filterString + "&filter[order]=created%20desc")
.success(function(data, status, headers, config) {
data.forEach(function(thread, i) {
$scope.threads[i] = thread;
var threadId = thread.id;
$http.get(ApiUrl.get() + '/Threads/' + threadId + '/posts/count?access_token=' + $scope.userToken)
.success(function(data, status, headers, config) {
$scope.threads[i].count = data.count;
})
.error(function(data, status, headers, config) {
alert('Connetion error, please try again.');
$location.path('/app/carte');
});
});
})
.error(function(data, status, headers, config) {
alert("Connection error, please try again.");
$location.path("/app/carte");
});
Because this uses forEach
(see the MDN page), thread
and i
are scoped to the function, so for the lifetime of that function and any functions that it calls or creates (like the promise success callback), thread
and i
will remain the value passed into the function. This ensures that no matter what order the HTTP requests return in, you'll be setting count
on the proper thread.