.= 'tag.htm'; break; case 'flag': $pre .= $default_pre .= 'flag.htm'; break; case 'my': $pre .= $default_pre .= 'my.htm'; break; case 'my_password': $pre .= $default_pre .= 'my_password.htm'; break; case 'my_bind': $pre .= $default_pre .= 'my_bind.htm'; break; case 'my_avatar': $pre .= $default_pre .= 'my_avatar.htm'; break; case 'home_article': $pre .= $default_pre .= 'home_article.htm'; break; case 'home_comment': $pre .= $default_pre .= 'home_comment.htm'; break; case 'user': $pre .= $default_pre .= 'user.htm'; break; case 'user_login': $pre .= $default_pre .= 'user_login.htm'; break; case 'user_create': $pre .= $default_pre .= 'user_create.htm'; break; case 'user_resetpw': $pre .= $default_pre .= 'user_resetpw.htm'; break; case 'user_resetpw_complete': $pre .= $default_pre .= 'user_resetpw_complete.htm'; break; case 'user_comment': $pre .= $default_pre .= 'user_comment.htm'; break; case 'single_page': $pre .= $default_pre .= 'single_page.htm'; break; case 'search': $pre .= $default_pre .= 'search.htm'; break; case 'operate_sticky': $pre .= $default_pre .= 'operate_sticky.htm'; break; case 'operate_close': $pre .= $default_pre .= 'operate_close.htm'; break; case 'operate_delete': $pre .= $default_pre .= 'operate_delete.htm'; break; case 'operate_move': $pre .= $default_pre .= 'operate_move.htm'; break; case '404': $pre .= $default_pre .= '404.htm'; break; case 'read_404': $pre .= $default_pre .= 'read_404.htm'; break; case 'list_404': $pre .= $default_pre .= 'list_404.htm'; break; default: $pre .= $default_pre .= theme_mode_pre(); break; } if ($config['theme']) { $conffile = APP_PATH . 'view/template/' . $config['theme'] . '/conf.json'; $json = is_file($conffile) ? xn_json_decode(file_get_contents($conffile)) : array(); } !empty($json['installed']) and $path_file = APP_PATH . 'view/template/' . $config['theme'] . '/htm/' . ($id ? $id . '_' : '') . $pre; (empty($path_file) || !is_file($path_file)) and $path_file = APP_PATH . 'view/template/' . $config['theme'] . '/htm/' . $pre; if (!empty($config['theme_child']) && is_array($config['theme_child'])) { foreach ($config['theme_child'] as $theme) { if (empty($theme) || is_array($theme)) continue; $path_file = APP_PATH . 'view/template/' . $theme . '/htm/' . ($id ? $id . '_' : '') . $pre; !is_file($path_file) and $path_file = APP_PATH . 'view/template/' . $theme . '/htm/' . $pre; } } !is_file($path_file) and $path_file = APP_PATH . ($dir ? 'plugin/' . $dir . '/view/htm/' : 'view/htm/') . $default_pre; return $path_file; } function theme_mode_pre($type = 0) { global $config; $mode = $config['setting']['website_mode']; $pre = ''; if (1 == $mode) { $pre .= 2 == $type ? 'portal_category.htm' : 'portal.htm'; } elseif (2 == $mode) { $pre .= 2 == $type ? 'flat_category.htm' : 'flat.htm'; } else { $pre .= 2 == $type ? 'index_category.htm' : 'index.htm'; } return $pre; } ?>javascript - How to properly implement an infinite scrolling AngularJS table without JQuery? - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to properly implement an infinite scrolling AngularJS table without JQuery? - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question edited May 23, 2017 at 12:02 CommunityBot 11 silver badge asked Apr 26, 2016 at 21:55 Uriel ArvizuUriel Arvizu 1,9266 gold badges41 silver badges98 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 6

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.

The problem with the solution provided by Uriel Arvizu is that if the raw element is empty because it is waiting for a Http Request at the page load, all the following raw.scrollTop, raw.clientHeight) and raw.scrollHeight will have wrong dimensions and the scroll is no working anymore.

I would suggest this other solution that basically adds the scroll event to the $window without cacheing it, so it is always sized correctly when the Http response is received.

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

Also, with this module, you can pass 2 parameters in the HTML: a callback function and a percentage of the specific element (where the module is applied) that when it is reached the callback function is called, i.e. to repeat the Http request (default is 90% of that element).

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

Using example code of Ferie, yet I had to use mousewheel event instead of scroll and had to use elem.scrollTop() instead of 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);
                    }
                });
            }
        };
    });

when used in Angular table with a ng-repeat on the <tr>, I had to add the directive into the parent tbody of this tr in order to capture the right element containing the scroll state.

<tbody infinite-scroll infinite-scroll-callback-fn="callBackFunction()" infinite-scroll-percentage="80">
发布评论

评论列表(0)

  1. 暂无评论