///Returning JSON 1
$http.get("url1").
then(function (response) {
$scope.foo = response.data;
});
///Returning JSON 2
$http.get("url2").
then(function (response) {
$scope.foo = response.data;
});
///Returning JSON (n)
$http.get("n").
then(function (response) {
$scope.foo = response.data;
});
Can I somehow concat these JSON objects into one? The reason is that I have ALOT of data and since I rather would like to display alot of data for the user to filter through than to have them click through 1000 pages in a SPA, I would like to join them if that's possible (in a reasonable manner ofcourse).
EDIT
I was thinking something like this
var url ="";
for (... i < 100...) {
url = "http://url.com"+i+"";
$http.get(url).
then(function(response){
$scope.foo.concat(response.data);
}
);
}
Update
I've managed to join the JSON returns into an array of objects. But the problem is that this array now contains objects which in itself contains an object which in itself contains an array of objects... yup!
If it's array then you can concat it.
Initialize empty array first
$scope.foo = [];
$http.get("url1").
then(function (response) {
$scope.foo.concat(response.data);
});
Use $q.all to create a promise that returns an array:
function arrayPromise(url, max)
var urlArray = [];
for (let i=0; i<max; i++) {
urlArray.push(url + i);
};
var promiseArray = [];
for (let i=0; i<urlArray.length; i++) {
promiseArray.push($http.get(urlArray[i]);
};
return $q.all(promiseArray);
});
To fetch nested arrays, chain from the parent:
function nestedPromise (url, max) {
var p1 = arrayPromise(url + "item/", max);
var p2 = p1.then(function(itemArray) {
var promises = [];
for (let i=0; i<itemArray.length; i++) {
var subUrl = url + "item/" + i + "/subItem/";
promises[i] = arrayPromise(subUrl, itemArray[i].length);
};
return $q.all(promises);
});
return p2;
};
Finally resolve the nested promise:
nestedPromise("https://example.com/", 10)
.then(function (nestedArray) {
$scope.data = nestedArray;
});
It is important to use a return statement at all levels of the hierarchy: in the .then methods and in the functions themselves.
Chaining promises
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
— AngularJS $q Service API Reference - Chaining Promises
You can wait for the n requests to finish and then do whatever you want with the object returned.
$q.all($http.get("url1"), $http.get("url2"), $http.get("url3"))
.then(function (responses) {
// This function is called when the three requests return.
// responses is an array with the first item being the result of
// fetching url1, the second fetching url2, etc.
// Depending on what the response looks like you may want to do
// something like:
$scope.data = angular.merge(responses[0], responses[1] /* etc */);
});
Related
I am working on function that calculates a distance from a given object to a number of other places with the help of Google Distance Matrix which is of course async therefore I'm dealing with promises.
When the number of places is one, everything works great. But once I have more than one promise, $q.all doesn't do anything : it neither resolves in a success nor in error. Though I have checked in the console that the calls to the Google distance matrix do happen and return a correct result. Any clue what can be at play here?
I am using AngularJS 1.6.4. Let me know should you need any more details. Thanks!
var requests = [];
for (var i = 0; i < ctrl.places.length; i += 1) {
var deferred = $q.defer();
requests.push(deferred.promise);
var destination = ctrl.places[i].latLng;
service.getDistanceMatrix({
origins: [ctrl.origin],
destinations: [destination[0] + "," + destination[1]],
travelMode: 'DRIVING'
}, function(response, status) {
if (status === 'OK') {
deferred.resolve(response.rows[0].elements[0].distance.text);
}
});
}
$q.all(requests).then(function(result) {
ctrl.distances = result;
});
Your problem is that var is not block-scoped, so the value of deferred will always belong to the final iteration of your loop by the time any of your callbacks are invoked. A consequence of this will that the earlier deferred will never be resolved and the $q.all will appear to hang.
The simplest way to resolve this is to change your use of var to let to take advantage of block scoping:
let deferred = $q.defer();
Reason why it does not work
By the time the service call resolves and the callback handler is invoked, the deferred attribute is referring to the very last deferred object created by the for loop. So, in effect, you are always performing a resolve on the very last deferred object that was created.
Solution:
Create a new function:
function getDistanceMatrixForDestination (destination, origins) {
var deferred = $q.defer();
service.getDistanceMatrix({
origins: [origins],
destinations: [destination[0] + "," + destination[1]],
travelMode: 'DRIVING'
}, function(response, status) {
if (status === 'OK') {
deferred.resolve(response.rows[0].elements[0].distance.text);
} else {
deferred.reject();
}
});
return deferred.promise;
}
Change your existing code to this:
var requests = [];
for (var i = 0; i < ctrl.places.length; i += 1) {
var destination = ctrl.places[i].latLng;
requests.push(getDistanceMatrixForDestination (destination, ctrl.origins));
}
$q.all(requests).then(function(result) {
ctrl.distances = result;
});
My code is like below,
// calls only the API and return it
s.getArchSales = function (url, qParam) {
// set the request object
var req = {
'method': 'POST',
'headers': securitySrv.header, // set the header
'url': url,
'data': qParam
}
return $http(req)
}
var portFolioMixArray = []
for(var i = 0; i < tech.length; i++ ){
s.getArchSales(url, query)
.then(function (response) {
portFolioMixArray.push(response.data)
})
}
tech is also an array which is also computed dynamically
Now when I console.log(portFolioMixArray) it shows Array[0] with an extension symbol at the left like below,
I can't access the Array elements. How can I access it ??
When you did console.log, at that time the http response was not yet received and the array was empty and hence you were not able to access the array elements.
Later, the response was received and array was updated. That's why you see the blue icon with the message.
You can get it by using portFolioMixArray[0]
if you want bind the values in html, then you need use ng-repeat
You should use the object in $scope
like
$scope.portFolioMixArray = []
for(var i = 0; i < tech.length; i++ ){
s.getArchSales(url, query)
.then(function (response) {
$scope.portFolioMixArray.push(response.data)
})
}
//html
<div ng-repeat="item in portFolioMixArray">{{item .YourObjectNmae}}</div>
I think you should understand the concept of promise, in Angular you can use $q.(https://docs.angularjs.org/api/ng/service/$q)
You can access your array like this:
var promises = tech.map(function (item) {
return s.getArchSales(url, query).then(function (response) {
portFolioMixArray.push(response.data)
})
}
$q.all(promises).then(function () {
console.log(portFolioMixArray);
}
while I'm getting a json object from Restangular ,another rest url function has been called(before first response comes)
Restangular.all("..").getList("..").then(
function(data){
$scope.dataList = data.dataList;
}, function errorCallback() {
alert("error");
}
);
here before initializing datalist it is calling another function parallely? how can I avoid this?
thanks.
If you need to call your services in specific order then you have two options:
Nesting service callbacks. This solution will prevent execution of the next ajax call until the previous is finished:
Restangular.all("..").getList("..").then(
function(data){
Restangular.secondFunction("..").getList().then(
function (data2) {
// both data and data2 are available
});
}, function errorCallback() {
alert("error");
}
);
Use $q.all() function which waits for the array of deferred objects to finish:
var promises = [];
promises.push(Restangular.all("..").getList(".."));
promises.push(Restangular.secondFunction("..").getList());
$q.all(promises).then(function (results) {
for (var i=0; i<results.length; i++)
{
// all ajax calls have finished now you can iterate through the results
}
});
BTW there is no such thing like parallel execution in javascript.
I am having some trouble chaining promises in Angular. What I want to do is fetch my project object from the API, then check if the project owner has any containers, if they do, trigger the another GET to retrieve the container. In the end the container assigned to scope should either be null or the object retrieved from the API.
Right now, this example below resolves immediately to the second then function, and I get the error, TypeError: Cannot read property 'owner' of undefined. What am I doing wrong?
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.project;
return data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $http.get('/api/containers/' + containers[0]);
} else {
return null
}
}).then(function (container) {
$scope.container = container;
});
Ah, turns out the data from passed into then is inside a field, so I needed to do
$scope.project = data.data.project;
return data.data.project;
Your example code works, but what if the $http call fails because of a 404? Or you want to later want to add some extra business logic?
In general you want to handle 'negative' cases using a rejecting promise, to have more control over the chaining flow.
$http.get('/api/projects/' + id).then(function (data) {
$scope.project = data.data.project;
return data.data.project;
}).then(function (project) {
var containers = project.owner.containers;
if (containers.length) {
return $q.reject('containers empty');
}
return $http.get('/api/containers/' + containers[0]);
}).then(function (container) {
$scope.container = container;
}).except(function (response) {
console.log(response); // 'containers empty' or $http response object
$scope.container = null;
});
I have a service with rest angular with following structure
function countrySvc(restangular) {
restangular.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
if (operation === 'getList') {
var newResponse = response.data;
return newResponse;
}
return response;
});
var baseCountry = restangular.all('country');
this.countries = function() {
baseCountry.getList();
};
}
also a controller
function countryCtrl(scope, countrySvc) {
scope.countries = countrySvc.countries();
}
but when i access the countries from controller, the result is empty with a successful request with data, my question is how a can extract the data from response with proper promise pattern, ie( i need array of countries when i access scope.countries)
You need to resolve promise...
There are two ways to do it...
1) Using $object
just add .$object to end of promise so once request is done it resolves promise...
scope.countries = countrySvc.countries().$object;
2) Using then
if you need to do some stuff after promise is resolved pick this option, once request is done callback function in then will be fired
scope.countries = countrySvc.countries().then(function (response){
// DO SOMETHING IF YOU NEED BEFORE SET OBJECT
scope.countries = response;
// DO SOMETHING IF YOU NEED AFTER SET OBJECT
});