Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

incremental UI updates from multiple promises

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?

like image 870
AnC Avatar asked Feb 23 '14 14:02

AnC


2 Answers

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);
like image 74
rob Avatar answered Nov 16 '22 01:11

rob


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

like image 23
Dan Saltmer Avatar answered Nov 16 '22 03:11

Dan Saltmer