1

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

  • https://docs.angularjs.org/api/ng/directive/ngCloak – Prasad Apr 14 '16 at 08:50
  • http://stackoverflow.com/questions/31083974/how-to-prevent-element-show-before-angularjs-initilized-ng-show this will help you for sure – Prasad Apr 14 '16 at 08:51
  • Thank you for response. But it is not helped...It still renders only $scope.users, and only then it console.log my data, that means $scope.users rendered before they updated... –  Apr 14 '16 at 09:01
  • You know as per the code you need to call back error i think Like // https://docs.angularjs.org/api/ng/service/$http error promise can serve your cause – Prasad Apr 14 '16 at 09:05
  • Thanks, I removed .error call for make code more tiny for StackOverflow... The problem is different - I cannot see changed data in my HTML –  Apr 14 '16 at 09:10
  • can you put your issue in plunker ? with some sample data i can take a look – Prasad Apr 14 '16 at 09:14

3 Answers3

2

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".

C14L
  • 12,153
  • 4
  • 39
  • 52
  • Seems simple, but it throws error: TypeError: Cannot set property 'error' of undefined: It cannot see $scope.users for some reason –  Apr 14 '16 at 09:45
  • Fixed it, for some reason the for loop was using $scope.jobs.length. Also, fixed "/jobs/" + scope. --> "/jobs/" + $scope. – C14L Apr 14 '16 at 09:50
  • Head Slap I Missed async part . C14L Thumbs Up!! :-) – Prasad Apr 14 '16 at 09:51
  • Amazing guys! Thanks, you all helped me! Now it works. I also binded value in HTML code, now it is rendered pretty as I wanted! –  Apr 14 '16 at 09:55
0

var user = $scope.users[i] only assign the value of $scope.users[i]. so later when you do user.error=..., it is not going to change the $scope. try this:

for (var i = 0; i < $scope.jobs.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 // this is not shows up in HTML!
    console.log(data.data.records[0].exception); // but it can be logged!
  })
}
Zhiliang Xing
  • 1,057
  • 1
  • 8
  • 19
0

2 elements about handling this :

If you use {{value}} replace this by

<span ng-bind="value"></span>

So you won't see brackets.

Now you want to wait for all your request before changing anything in order to not have partial data displayed. This is easy too :

var promiseArray = []; 
for (var i = 0; i < $scope.users.length; i++) {
     var path = "/jobs/" + scope.users[i].id + "/sessions";
     var user = $scope.users[i]
     promiseArray.push(toApi.get(path)).success(function(data){
         var result = {i:i, data:data};
         return result
     });
 }
 $q.all(promiseArray).then(function success(result){
     for(var i=0; i < promiseArray.length;i++){
        $scope.users[result.i].error = result.data.data.records[0].exception;
     }
 });

Bascially i'm waiting for all request to be resvoled before changing any data so this will run in the same angular loop. So the DOM will be only refreshed once and the user won't see any partial results.

Walfrat
  • 5,363
  • 1
  • 16
  • 35