Reputation: 32818
I have code that creates a grid like this:
<div ng-repeat="row in home.grid.data track by row.examId">
<div>{{ row.examId }}</div>
<div>xxxx</div>
</div>
I have more columns after these.
Is there a way I can speed up the way my page reacts? It seems that when I have a lot of data in the grid then the pages reacts slowly. Would it make a difference if I used ng-model in an input type field for the row.examId. Note that some of the fields that follow can be edited but most are just display only.
Upvotes: 7
Views: 959
Reputation: 7663
This is what I have done. There are two ways. Irrespective of both solutions, use bindOnce. Keep a look out on the number of watchers on the page. Look at the end of this solution - how to keep track of watchers on a page.
I have added a solution 3 and this is working awesome, styling is a bit difficult
Solution 1:
Use a pagination control with bind once.
Solution 2 This is what worked for me and it is very elegant. You repeat with bindonce and then implement infinite scrolling. I have followed this blog post and it works like a charm. The idea is you limit the number of rows and change the limit as you scroll.
ng-repeat="item in items | orderBy:prop | filter:query | limitTo:limit"
Essentially, your html would look like this. I have modified the OP's code to use bindonce.
<div id="estates-listing" extend-height>
<div class="content" infinite-scroll="addMoreItems()" infinite-scroll-distance="2">
<div class="content-wrapper">
<div class="house" bindonce="estate" ng-animate="'animate'" ng-class="{inactive: (selectedEstate != null || selectedEstate != undefined) && estate.id!=selectedEstate.id , active:(selectedEstate != null || selectedEstate != undefined) && estate.id==selectedEstate.id}" ng-repeat="estate in estates | orderBy: orderProp : orderReverse | limitTo: config.itemsDisplayedInList track by estate.id" ng-mouseover="highlightMarker(estate)" ng-mouseleave="leaveMarker(estate)" ng-click="showDetailview(estate.id)" >
<div id="l-el{{estate.id}}">
</div>
</div>
</div>
</div>
</div>
Here is the infinite scroll directive from the post. Add this to your app, please don't use the standard infinite scroll using bower install.
app.directive('infiniteScroll', [
'$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) {
return {
link: function(scope, elem, attrs) {
var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
$window = angular.element($window);
elem.css('overflow-y', 'scroll');
elem.css('overflow-x', 'hidden');
elem.css('height', 'inherit');
scrollDistance = 0;
if (attrs.infiniteScrollDistance != null) {
scope.$watch(attrs.infiniteScrollDistance, function(value) {
return scrollDistance = parseInt(value, 10);
});
}
scrollEnabled = true;
checkWhenEnabled = false;
if (attrs.infiniteScrollDisabled != null) {
scope.$watch(attrs.infiniteScrollDisabled, function(value) {
scrollEnabled = !value;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
});
}
$rootScope.$on('refreshStart', function(event, parameters){
elem.animate({ scrollTop: "0" });
});
handler = function() {
var container, elementBottom, remaining, shouldScroll, containerBottom;
container = $(elem.children()[0]);
elementBottom = elem.offset().top + elem.height();
containerBottom = container.offset().top + container.height();
remaining = containerBottom - elementBottom ;
shouldScroll = remaining <= elem.height() * scrollDistance;
if (shouldScroll && scrollEnabled) {
if ($rootScope.$$phase) {
return scope.$eval(attrs.infiniteScroll);
} else {
return scope.$apply(attrs.infiniteScroll);
}
} else if (shouldScroll) {
return checkWhenEnabled = true;
}
};
elem.on('scroll', handler);
scope.$on('$destroy', function() {
return $window.off('scroll', handler);
});
return $timeout((function() {
if (attrs.infiniteScrollImmediateCheck) {
if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
return handler();
}
} else {
return handler();
}
}), 0);
}
};
}
]);
Solution 3: Be adventurous and use UI-Grid, UI Grid is the new ng-grid. It is not production ready, but we are playing around in production in a table where we have over 1000 records- out of the box it is awesome. The tutorials are extensive but not much SO support. It has virtualization in built and since it is an extension of ng-grid, it has a lot of backward compatibility. Here is a example with 10,000 rows
Number of watchers on the page: Here is a function to track the number of watchers on the page. The thumb rule is never exceed 2500 watchers, but we restrict ourselves to < 1000.
$scope.TotalWatchers = function () {
var root = $(document.getElementsByTagName('body'));
var watchers = 0;
var f = function (element) {
if (element.data().hasOwnProperty('$scope')) {
watchers += (element.data().$scope.$$watchers || []).length;
}
angular.forEach(element.children(), function (childElement) {
f(angular.element(childElement));
});
};
f(root);
return watchers;
};
Upvotes: 4
Reputation: 3763
The biggest thing I've found to help with performance of large tables is to limit event binding to the parent object and make use of bubbling to capture the events of the children.
In the event of the parent you can get which target was hit. I use the following code.
obj.onclick = function (e) {
e = window.event || e;
var t = e.target || e.srcElement;
}
in this event e is your regular event object and t is the object that was the initial target before the event bubbled. You need to use t as 'this' references the object that the event is bound to not the object that triggered the event.
Where I was using the code which was a really large table it reduced the rendering time of the table by almost 80% by moving the events to a parent node that was static. This also helps if you need to update the contents as you don't have to re-bind any events.
Hope this helps.
Upvotes: 1