如何在没有JQuery的情况下正确实现无限滚动的AngularJS表

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

本文关键字:实现 无限 滚动 AngularJS 情况下 JQuery      更新时间:2023-09-26

我有一个AngularJS项目(我不使用JQuery),我需要显示一个包含用户的表格,并在用户滚动到页面末尾时加载更多。我正在尝试在不依赖外部库的情况下实现这一点,因为这是我要求的一部分。

我已经检查了几个像这样,这个和这个的例子。

但到目前为止,我检查过的所有示例都没有帮助我实现我预期的无限滚动。他们使用不同的方法来计算何时触发调用,其中一些值返回未定义给我(即人们说clientHeight和scrollHeight有不同的值,但对我来说它总是相同的,包括滚动空间的总高度)。

我创建了一个如下所示的指令:

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);
              })
            }
          });
        }
    };
});

这种工作,但有以下问题/疑问,我希望有人可以向我解释:

  • 它触发得太快。我希望加载在我接近可滚动空间的底部时触发,例如接近 90% 左右。
  • scrollHeight 只能通过 elm[0] 访问,angular.element($window)[0] 没有 scrollHeight 属性,因此它返回 undefined,elm[0] 没有 scrollY 值。
  • 我得到的scrollY值是滚动条从顶部移动的距离,减去滚动条长度,但我觉得这个值是错误的。
  • 通过 angular.element($window).bind 绑定滚动事件是正确的决定吗?

如何实现适当的无限滚动表?我是否使用了正确的变量?请提供一个仅使用Javascript和AngularJS的答案,JQuery或库解决方案对我无济于事。

在检查了几个示例并试图避免那些使用 JQuery 作为解决方案一部分的示例后,我找到了一种方法来实现这一目标。

首先是我的指令,它将在滚动结束时处理:

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);
            }
          });
        }
    };
});

我的视图将表放在div内,如下所示:

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

确保包含表的div是滚动结束时侦听的。此div必须具有设置的高度,如果没有,则 clientHeight 属性将与 scrollHeight 相同(不确定如果没有显式定义高度会发生什么,例如不是设置高度而是设置顶部或底部属性)。

在我的控制器中,loadMoreUsers()负责递增页面(如果有更多的用户,我收到的每个调用都有用户总数,所以我可以在发出另一个请求之前知道我还剩下多少用户),它还调用向 Web 服务发出请求的函数。

Uriel Arvizu 提供的解决方案的问题在于,如果 raw 元素为空,因为它在页面加载时等待 Http 请求,则所有以下raw.scrollTopraw.clientHeight)raw.scrollHeight都将具有错误的尺寸,并且滚动不再工作。

我会建议另一种解决方案,它基本上将滚动事件添加到$window而不缓存它,因此在收到 Http 响应时它的大小总是正确的。

(function () {
    'use strict';
    angular.module('ra.infinite-scroll', []).directive('infiniteScroll', function ($window) {
        return {
            restrict: 'A',
            scope: {
                infiniteScrollCallbackFn: '&'
            },
            link: function (scope, elem, attrs) {
                var percentage = (attrs.infiniteScrollPercentage !== undefined ? (attrs.infiniteScrollPercentage / 100) : '.9');
                var $element = elem[0];
                angular.element($window).on('scroll', function () {
                    if ((this.scrollY + this.innerHeight - $element.offsetTop) >= ($element.scrollHeight * percentage)) {
                        scope.$apply(scope.infiniteScrollCallbackFn);
                    }
                });
            }
        };
    });
})();

此外,使用此模块,您可以在 HTML 中传递 2 个参数:一个回调函数和特定元素(应用模块的位置)的百分比,当到达回调函数时,将调用该元素,即重复 Http 请求(默认值为该元素的 90%)。

    <div infinite-scroll infinite-scroll-callback-fn="callBackFunction()" infinite-scroll-percentage="80">
        // Here you may include the infinite repeating template 
    </div>
使用

Ferie 的示例代码,但我不得不使用 mousewheel 事件而不是 scroll 并且不得不使用 elem.scrollTop() 而不是 elem.scrollY

app.directive('infiniteScroll', function ($window) {
        return {
            restrict: 'A',
            scope: {
                infiniteScrollCallbackFn: '&'
            },
            link: function (scope, elem, attrs) {
                var percentage = (attrs.infiniteScrollPercentage !== undefined ? (attrs.infiniteScrollPercentage / 100) : '.9');
                var $element = elem[0];
                angular.element($window).on('mousewheel', function () {
                    if ((elem.scrollTop() + this.innerHeight - $element.offsetTop) >= ($element.scrollHeight * percentage)) {
                        scope.$apply(scope.infiniteScrollCallbackFn);
                    }
                });
            }
        };
    });

当在 Angular 表中使用时,<tr>上带有 ng-repeat,我必须将指令添加到此tr的父tbody中,以便捕获包含滚动状态的正确元素。

<tbody infinite-scroll infinite-scroll-callback-fn="callBackFunction()" infinite-scroll-percentage="80">