6

I'm trying to make a directive that I can do a virtual scroll, so as the user scrolls the table, the table remove "old" views and add "new" views, kind like of collection repeat but I've been failing, I think I didn't understand the math behind it, can someone help me?

this is my directive code:

BaseModule.directive('myScroll', function() {
    return {
        restrict:"A",
        scope:{
            rows:"=",
            headers:"="
        },
        link: function(scope,el) {
            var scrollTop = 0;
            var scrollLeft = 0;
            angular.element(el).on('scroll',function(){
                scrollTop = $(el).scrollTop();
                scrollLeft = $(el).scrollLeft();
                $(el).parent().find(".header").scrollLeft(scrollLeft);
                var height = $(el).height();
                var numberOfRows = height/23;
                var initialRow = scrollTop/23;
                var html = "";
                for(i=0; i<numberOfRows;i++){
                    var row = scope.rows[i+initialRow];
                    html = html + addRow(row,i+initialRow);
                }
                $(el).find('.tbody-scroll').append(html);
            });
            scope.$watch('rows',function(rows){
                var height = $(el).height();
                var numberOfRows = height/23;
                var initialRow = scrollTop/23;
                var html = "";
                for(i=0; i<numberOfRows;i++){
                    var row = rows[i+initialRow];
                    html = html + addRow(row,i+initialRow);
                }
                $(el).find('.tbody-scroll').append(html);
            });
            var addRow = function(row,index){
                var html = "";
                var pos = 0;
                var totalWidth = 0;
                angular.forEach(row,function(col){
                    var width = scope.headers[pos].width;
                    totalWidth = totalWidth + width;
                    html = html + "<span style='width:"+width+"px'>"+col.value+"</span>";
                    pos++;
                });
                html = "<div class='row' style='top:"+index*23+"px;width:"+totalWidth+"px;'>"+html;
                html = html + "</div>";
                return html;
            };
        }
    };
});

<!-- my directive .html -->
<div class="mTable">
    <div class="header" ng-style="headerWidth(headers())">
        <span ng-repeat="header in headers()" ng-style="widthStyle(header)">
            {{::header.label}}
        </span>
    </div>
    <div class="tbody-container" my-scroll headers="headers()" rows="rows()">
        <div class="tbody-scroll" ng-style="scrollHeight(rows(),headers())"></div>
    </div>
</div>
dhilt
  • 13,532
  • 6
  • 48
  • 67

2 Answers2

6

Giving a full answer with code might require a bit too much of an effort
This library implements virtual scroll on ng-repeat https://github.com/stackfull/angular-virtual-scroll in the description there's also an article on how to implement this feature on your own.

The basic concept is to make two divs, one above and one below the list, which size is determined by the number of elements inside the list (this approach has a limitation since list elements must either have all the same height or their height must be fixed), then you delete elements as they disappear from the viewport and resize the divs according to the number of elements not currently rendered and your position on the list

valepu
  • 2,735
  • 4
  • 30
  • 57
  • 1
    I actually already saw this directive before, and had no success but suddenly I realized it didn't worked only because of the css of divs and now I got it going, so I'll accept yours – João Pedro Segurado Oct 21 '15 at 12:53
  • Glad I have been of help, somehow – valepu Oct 21 '15 at 12:54
  • 1
    You actually did, seeing this directive again made me think "try again bro" so thanks!!! – João Pedro Segurado Oct 21 '15 at 13:03
  • 1
    alternatively you can not add a divs at all but use a transform: translateY to shift the position of the div containing all your list items, this method is GPU efficient as you are not changing heights, check this pen https://codepen.io/zupkode/pen/vYEPvrN – PirateApp Feb 06 '20 at 04:52
3

Not a direct answer to your question, but an alternative: You may want to look at the ui-scroll directive which is a replacement for ng-repeat and has a similar function.

Example in your controller

$scope.movieDataSource = {

    get : function (index, count, callback) {
        var i, items = [$scope.userMovies], item;

            var min = 1;
            var max = 1000;

        for (i=index ; i<index + count ; i++) {
                if(i < min || i > max) {
                    continue;
                }
            item = {
                title: $scope.userMovies.title,
                imageURL: $scope.userMovies.poster_path
            };
            items.push (item);
        }
        callback (items);
    }
}

And in your view:

<div ui-scroll="item in movieDataSource">
  {{item.title}}
</div>
Andre Kreienbring
  • 2,317
  • 9
  • 15