Skip to content

Instantly share code, notes, and snippets.

@scyrizales
Created May 5, 2016 00:06
Show Gist options
  • Save scyrizales/f2a297db522e2d789f69f70e7151ce7e to your computer and use it in GitHub Desktop.
Save scyrizales/f2a297db522e2d789f69f70e7151ce7e to your computer and use it in GitHub Desktop.
SimpleVirtualRepeater - This is an simple solution to handle thousands of results on an ng-repeat without sacrificing performance.
(function() {
'use strict';
angular.module('simple-virtual-repeater', [])
.directive('virtualRepeater', virtualRepeater);
virtualRepeater.$inject = ['$compile', '$timeout'];
function virtualRepeater($compile, $timeout) {
var directive = {
restrict: 'EA',
replace: 'true',
transclude: true,
template: '<div class="virtual-repeat"><div class="virtual-repeat-before"></div><div ng-transclude=""></div><div class="virtual-repeat-after"></div></div>',
link: function postLink(scope, iElement, iAttrs, controller) {
var internalRepeat = iElement.find("[ng-transclude]"),
beforeRepeat = iElement.find(".virtual-repeat-before"),
afterRepeat = iElement.find(".virtual-repeat-after"),
parent = iElement.parent();
internalRepeat.hide();
var expressionMatches = /^\s*(\S+)\s+in\s+([\S\s]+?)(track\s+by\s+\S+)?$/.exec(iAttrs.virtualRepeat),
lhs = expressionMatches[1],
rhs = expressionMatches[2],
rhsSuffix = expressionMatches[3] || '';
var itemsAllowed = +(iAttrs.itemsAllowed || 5), totalItems = [], itemHeight = 0;
var repeater = angular.element('<div class="virtual-repeat-item" ng-repeat="' + lhs + ' in virtualRepeatCollection ' + rhsSuffix + '"></div>');
repeater.html(internalRepeat.html());
scope.virtualRepeatCollection = [];
scope.$watchCollection(rhs, function (data) {
if (data) {
totalItems = data;
scope.virtualRepeatCollection = totalItems.slice(0, itemsAllowed);
$timeout(function () {
itemHeight = iElement.find(".virtual-repeat-item:first").height();
parent.scrollTop(0);
beforeRepeat.css("height", 0);
afterRepeat.css("height", itemHeight * (totalItems.length - itemsAllowed));
});
}
});
internalRepeat.after($compile(repeater)(scope));
parent.on('scroll', function () {
var movedDistance = parent.scrollTop(),
movedItems = Math.floor(movedDistance / itemHeight);
movedItems = (movedItems > 0)? movedItems - 1 : 0;
scope.virtualRepeatCollection = totalItems.slice(movedItems, movedItems + itemsAllowed);
beforeRepeat.css("height", itemHeight * (movedItems));
afterRepeat.css("height", itemHeight * (totalItems.length - movedItems - itemsAllowed));
scope.$apply();
});
}
};
return directive;
};
})();
@scyrizales
Copy link
Author

If you want to use you should do this:
replace your

<div ng-repeat="item in collection"></div>

with

<div>
    <div virtual-repeat="item in collection" items-allowed="5"></div>
<div>

items-allowed is the ammount of items you gonna display... Play with it until you find how many items you need to fill the container div.

I hope you like it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment