Search code examples
javascriptangularjsangular-resource

How to prevent angular to render page until all data will be present?


I have following issue: I trying to "merge" data from two collections from MongoDB. Everything looks OK until I try to show some data from second query that came exactly after first one:

$scope.getData = function() {
 var pipeline, fromDateRangeStatement, toDateRangeStatement, service, joblist;
 service = ($scope.service && $scope.service.value) ? $scope.service.value : {
     $exists: true
 };
 pipeline = $scope.utils.getSessionUsersPipeline($scope.fromDate, $scope.toDate,
     $scope.checkedResults, $scope.checkedStatuses, service, $stateParams, $scope.usersLimit);
 toApi.post("/users/aggregate", {
     pipeline: pipeline
 }).success(function(data) {
     $scope.users = data.data.result;
     for (var i = 0; i < $scope.users.length; i++) {
         var path = "/jobs/" + scope.users[i].id + "/sessions";
         var user = $scope.users[i]
         toApi.get(path).success(function(data) {
             user.error = data.data.records[0].exception // this is not shows up in HTML!
             console.log(data.data.records[0].exception); // but it can be logged!
         })
     }
 })

};

So, problem is: $scope.users are rendered at my page, while their attribute error is not. Looks like data became rendered before I change attributes for every user in for loop. How this can be handled? Thanks


Solution

  • Below are two different solutions


    Each of your GET requests is async, so you need to wait for all of them to resolve. You could wrap each request in its own Promise and collect all Promises like so

    var promises = [];
    for (var i = 0; i < $scope.users.length; i++) {
      promises[i] = new Promise(function (resolve, reject) {
        var path = "/jobs/" + scope.users[i].id + "/sessions";
        var user = $scope.users[i]
        toApi.get(path).success(function(data){
          user.error = data.data.records[0].exception;
          console.log(data.data.records[0].exception);
          resolve(user);
      })
    }
    

    Then use Promise.all() to group all promises together as one Promise and wait for it to resolve.

    Promise.all(promises).then(function(users) { 
      $scope.users = users;
    });
    

    Quick example: http://codepen.io/C14L/pen/QNmQoN


    Though, a simpler and better solution would be to display the users one after one, as their API calls return

    for (var i = 0; i < $scope.users.length; i++) {
      var path = "/jobs/" + $scope.users[i].id + "/sessions";
      toApi.get(path).success(function (data) {
          $scope.users[i].error = data.data.records[0].exception;
          $scope.users[i].done = true;
          console.log(data.data.records[0].exception);
      });
    }
    

    Simply add into your template a check for $scope.users[i].done = true and ony show that dataset with ng-show="user.done".