I have an AngularJS service that's used to retrieve individual contacts (/contacts/:id
) based on an index (/contacts
):
app.service("CollectionService", function($http, $q) {
this.fetch = function(collectionURL) {
return $http.get(collectionURL).then(function(response) {
var urls = response.data;
var entities = urls.map(function(url) {
return $http.get(url);
});
return $q.all(entities);
}).then(function(responses) {
return responses.map(function(response) {
return response.data;
});
});
};
});
// used within the controller:
CollectionService.fetch("/contacts").then(function(contacts) {
$scope.contacts = contacts;
});
The results are displayed in a simple list (<li ng-repeat="contact in contacts">{{ contact }}</li>
).
However, due to the use of $q.all
, that list is not updated until the last (slowest) response has been received. How would one switch from this bulk update to incremental updates as individual contacts are received?
You could just pass the contacts list into fetch() and have it popupate the list.
app.service("CollectionService", function($http, $q) {
this.fetch = function(collectionURL, resultList) {
$http.get(collectionURL).then(function(response) {
var urls = response.data;
urls.forEach(function(url) {
$http.get(url).then(function(response) {
resultList.push(response.data);
});
});
};
};
});
// used within the controller:
$scope.contacts = [];
CollectionService.fetch("/contacts", $scope.contacts);
You could use your own promise to return and then hook into a notification of the promise to give you an update on the overall loading progress, and still use $q.all
to determine when this has finished. It is basically what you have now with slightly different way of handling and using a custom promise.
Fiddle: http://jsfiddle.net/U4XPU/1/
HTML
<div class="wrapper" ng-app="stackExample">
<div class="loading" ng-show="loading">Loading</div>
<div class="contacts" ng-controller="ContactController">
<div class="contact" ng-repeat="contact in contacts"> {{contact.name}} - {{contact.dob}}</div>
</div>
</div>
Controller
.controller("ContactController", ["$scope", "CollectionService", function (scope, service) {
scope.contacts = [];
scope.loading = true;
service.fetch("/contacts")
.then(
// All complete handler
function () {
console.log("Loaded all contacts");
scope.loading = false;
},
// Error handler
function () {
scope.error = "Ruh roh";
scope.loading = false;
},
// Incremental handler with .notify
function (newContacts) {
console.log("New contacts found");
scope.contacts = scope.contacts.concat(newContacts);
});
}])
Service
.service("CollectionService", ["$q", "$http", function (q, http) {
this.fetch = function (collectionUrl) {
var deferred = q.defer();
http.get(collectionUrl)
.then(function (response) {
// Still map all your responses to an array of requests
var allRequests = response.data.map(function (url) {
return http.get(url)
.then(function (innerResponse) {
deferred.notify(innerResponse.data);
});
});
// I haven't here, but you could still pass all of your data to resolve().
q.all(allRequests).then(function () {
deferred.resolve();
});
});
return deferred.promise;
}
}]);
You can also handle the errors as you see fit and .reject()
the promise:
http://docs.angularjs.org/api/ng/service/$q
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With