Uriel Arvizu Uriel Arvizu - 7 months ago 157
Javascript Question

How to properly implement an infinite scrolling AngularJS table without JQuery?

I have an AngularJS project (I don't use JQuery) where I need to display a table with users and load more as the user scrolls near the end of the page. I'm trying to implement this without relying on external libraries, since this is part of my requirements.

I've checked several examples like this, this and this.

But so far, none of the examples I've checked have helped me implement the infinite scrolling as I expected. They use different ways to calculate when to trigger the call, and some of this values return undefined to me (i.e. people say clientHeight and scrollHeight have different values, but to me it's always the same one, the total height including scrolling space).

I created a directive like the following:

usersModule.directive('whenScrollEnds', function($window) {
return {
restrict: "A",
link: function(scope, elm, attr) {

angular.element($window).bind('scroll', function() {

var hiddenContentHeight = elm[0].scrollHeight - angular.element($window)[0].innerHeight;

if ((hiddenContentHeight - angular.element($window)[0].scrollY) <= (10 )) {
angular.element($window)[0].requestAnimationFrame(function(){
scope.$apply(attr.whenScrollEnds);
})
}

});
}
};
});


This kind of works but there's the following problems/doubts which I hope someone can explain to me:


  • It triggers too fast. I want the loading to trigger when I'm near the bottom of the scrollable space, like near 90% or so.

  • The scrollHeight is only accesible through elm[0], angular.element($window)[0] has no scrollHeight property so it returns undefined, and elm[0] has no scrollY value.

  • The scrollY value I get is the distance the scrollbar has moved from the top, minus the scrollbar length, but I feel like that value is wrong.

  • Is binding the scroll event through angular.element($window).bind the right decision?



How can I implement a proper infinite scrolling table? Am I using the correct variables? Please provide an answer that uses Javascript and AngularJS only, JQuery or libraries solutions won't help me.

Answer

After checking several examples and trying to avoid those that used JQuery as part of the solution I found a way to make this work.

First my directive that would handle when the scroll ends:

usersModule.directive('whenScrollEnds', function($window) {
    return {
        restrict: "A",
        link: function(scope, elm, attr) {
          var raw = elm[0];

          raw.addEventListener('scroll', function() {
            if ((raw.scrollTop + raw.clientHeight) >= (raw.scrollHeight )) {
                scope.$apply(attr.whenScrollEnds);
            }
          });
        }
    };
});

My view has the table inside a div like this:

  <div id="usersScrollable" when-scroll-ends="uc.loadMoreUsers()">
    <table id="tableUsers" class="table" ng-show="uc.users.length" >
      <thead>
      <tr>
        <th>Email</th>
        <th>Nombre Completo</th>
        <th>Estado</th>
        <th>Acción</th>
      </tr>
      </thead>
      <tbody >
      <tr ng-repeat="u in uc.users">
        <td>{{u.email}}</td>
        <td>{{u.fullName}}</td>
        <td>{{uc.getStateString(u.active)}}</td>
        <td><button type="button" class="btn btn-primary" ng-click="uc.edit(u)">Editar</button></td>
      </tr>
      </tbody>
    </table>
  </div>

Make sure the div that contains the table is the one listening to when the scrolling ends. This div has to have a set height, if it doesn't then the clientHeight property will be the same as scrollHeight (not sure what would happen if there's no height defined explicitly, like instead of setting the height you set the top or bottom properties).

In my controller, loadMoreUsers() is in charge of incrementing the page (in case there's more users, each call I get has the total of users so I can know before making another request how many users I have left), also it calls the function that makes the request to the web service.

Comments