1

In mainPage.html we are dispaying a table from an object names.

 <table>
  <tr ng-repeat="x in names">
  <td>{{ x.Name }}</td>
  <td>{{ x.Country }}</td>
  </tr>
 </table>

and when the user clicks on next button a rest request for next set of names is being dispatched. I am displaying a loading image immediately after clicking on the next Button. I want to hide the loading image only after the new set of data is reflected in the table. How can I know if angular has finished updating the DOM with the new set of data?

Divya MV
  • 2,021
  • 3
  • 31
  • 55

4 Answers4

3

As soon as the $http call ends, Angular will trigger a digest phase (via $apply). Any code executed after that digest phase will be executed after the HTML has been updated.

One way to make sure your code is executed after the digest phase is to use setTimeout or $timeout. For instance:

$http.get(url).success(function() {
  // Update the scope
  $scope.data = data;
  // Wait for the DOM to be up do date (end of digest cycle)
  $timeout(function() {
    // Here you can safely remove your loading things
  });
});

However if the image itself is added/removed by Angular (using ng-show or ng-if for instance), then you don't event need that $timeout: the image will be hidden from the view at the same time as the view is being updated. This will happen in the same digest cycle.

floribon
  • 19,175
  • 5
  • 54
  • 66
  • @floribbon i found out one more answer to the question.Can you see which one is a performant. http://stackoverflow.com/a/15208347/4260544 – Divya MV Apr 16 '15 at 08:17
  • 1
    @DivyaMV I have already seen this answer and to be honest and do not understand the need for it. `ng-repeat` will compile all the items at the same time, not one by one with any refresh in between. It happens during the same digest cycle so a `$timeout` is enough, no need for that extra directive. Regarding performances they should have the same, or a simple `$timeout` as in my answer would be a bit faster since you avoid a directive compile/link phases + event emission. – floribon Apr 16 '15 at 18:40
2

Im not really sure if that helps you but some1 created a onNg-repeatfinished Handler maybe u can apply this for ur table but im not sure ngrepeatfinishedHandler

Community
  • 1
  • 1
stackg91
  • 584
  • 7
  • 25
1

I'm making the assumption that you are requesting the next set of names via $http. If not, the same pattern applies with async actions in general, as long as you use promises.

With the following, everything happens in the Angular runtime. The data point for the show/hide is updated at the same time as names and thus they will update in the same digest cycle, and thus render at the same time on the DOM.

In general, keeping everything in angular and keying everything off the $scope in your view makes it easy to keep things in sync.

.controller('tableController', function($http, $scope){
    $scope.loading = false;
    $scope.getSomeNames = function(){
       $scope.loading = true; 
       $http.get('names').then(function(reponse){
            $scope.loading = false;
            $scope.names = response.data
       })
    })

})

HTML:

<table ng-controller="tableController">
  <tr ng-repeat="x in names" ng-hide="loading">
  <td>{{ x.Name }}</td>
  <td>{{ x.Country }}</td>
  </tr>
  <tr ng-show="loading"><td>Loading</td></tr
</table>
Dylan Watt
  • 3,357
  • 12
  • 16
  • 1
    answer is not right `I want to hide the loading image only after the new set of data is reflected in the table` its not on $http end, but on page refresh – harishr Apr 16 '15 at 07:09
  • 1
    also `$scope.loading = false;` should always be in finally – harishr Apr 16 '15 at 07:09
  • But since both the image flag and the data update occur at the same time the DOM will update with both points of data simultaneously there's no way for the loading indicator to go away before the data shows up. Also, whether you want to have the loading indicator go away on error is a matter of preference. – Dylan Watt Apr 16 '15 at 07:16
  • page refresh? When is there a page refresh? – tpie Apr 16 '15 at 07:24
  • 2
    no if you have lets say 1000s of rows in your response, then the time when the $http finishes, and the time by when angular's digest cycles get over.. there would be a significant lag... what the question ask is - how to identify when the last digest cycle has finished – harishr Apr 16 '15 at 07:28
  • @tpie : sorry not page refresh, but when angular's all digest cycles finishes – harishr Apr 16 '15 at 07:29
  • See http://codepen.io/anon/pen/WbVLVq , it's all going to be a synchronous action from when $http finishes till when the DOM is rendered with the new data.. If you can show me an example of when this does not happen, I'd be grateful, as everything I know about angular implies that you can expect the DOM to update with the same patterns as native javascript. That is, to say, it never updates during a synchronous javascript execution. – Dylan Watt Apr 16 '15 at 07:42
  • check out my updated answer...particularly the last bit. The directive is the ticket, I think. – tpie Apr 16 '15 at 07:49
  • I've simply never had the issue being described here. At what point during the digest cycle after the $http success is the javascript thread be asynced such that the DOM has a chance to render in a "in between" state? Indeed, if that occurred ng-repeats would render one at a time, but in reality they render out all at once. – Dylan Watt Apr 16 '15 at 07:53
  • @DylanWatt it does happen.. thats the very reason somebody is asking question [like this](http://stackoverflow.com/questions/15207788/calling-a-function-when-ng-repeat-has-finished) – harishr Apr 16 '15 at 07:59
  • I'd love it if you could provide me a fiddle of this. I provided one with 10000 elements being ng-repeated that showed that `loading` went away at the same as when the repeat rendered. Digests cycles are synchronous. The DOM is incapable of rendering during a javascript synchronous thread. If angular behaved in the way implied here, it would be pretty much unusable, as everything would be constantly bouncing around. – Dylan Watt Apr 16 '15 at 08:04
  • @DylanWatt it somewaht depends on the environment that you use i belive.in our `local env` the bindings happen as soon as we obtain the new set of data.There is no lag at all.But in our `test env` there is a noticeable minute time lag which shows the first data in the page before displaying the new set of data. – Divya MV Apr 16 '15 at 08:11
  • @tpie second part of your answer anwers my questions .same is floribbon's answer. – Divya MV Apr 16 '15 at 08:13
  • @DivyaMV it shows that using the loading flag strategy that I suggest, or previously? Certainly during the call out old data will be shown without setting a flag, but locally it would be fast enough to not really be needed. Setting a loading flag has always worked for me (with sets of thousands of users, each with fairly large amounts of data associated with them). – Dylan Watt Apr 16 '15 at 08:14
  • @DylanWatt `ng-hide="loading"` with this in place it will never show the old data. – Divya MV Apr 16 '15 at 08:20
0

I would agree with Dylan Watt, though I think I would want to use $http's built promise, with success and error for a little cleaner handling if there is a problem.

Set a var that enabled the loading state, make your ajax call, change the loading state back on success of that ajax call (or do something else in case of an error).

$scope.getFunction = function () {
  $scope.loading = true;
  $http.get('/someUrl').
    success(function(data, status, headers, config) {
      $scope.names = data;
      $scope.loading = false;
    }).
    error(function(data, status, headers, config) {
      // gracefully handle the error here
    });
}

If the above doesn't solve your issue, and the following is your concern:

no if you have lets say 1000s of rows in your response, then the time when the $http finishes, and the time by when angular's digest cycles get over.. there would be a significant lag... what the question ask is - how to identify when the last digest cycle has finished – entre

One potential solution (though I am not convinced this is a great way) would be something along the lines of this post: How to watch the DOM for changes AngularJS

and set a timeout to wait until no DOM changes occur for a set period of time. I have not seen anywhere that you can identify when the last $digest cycle occurs specifically. In the case of large amounts of data being blasted through ng-repeat, you should see a burst of DOM population, and then it should go quiet.

A better solution seems to be to use an 'element-ready' directive which triggers the $rootScope.loading = false once it has loaded, as described in the following post: Sending event when angular.js finished loading

Community
  • 1
  • 1
tpie
  • 6,021
  • 3
  • 22
  • 41
  • 1
    I usually avoid the built in success & error methods because it makes it hard to port your logic to another data source. If it's coming from websockets for instance, you have to rewrite the service, where if you use the standard promise, you can just swap out the one call. A matter of preference, of course. – Dylan Watt Apr 16 '15 at 07:19