Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS performance: How to update only the scopes I know need to be updated?

Tags:

angularjs

I have an angular scope containing two things:

  • a giant table with 10k rows that takes a second to render
  • some small extra information on top of it in a fixed overlay header bar

Depending on how far you have scrolled down the page/table, I have to update one of the small info bits in the header; you can think of it as a % counter how far you've scrolled.

To get the current scroll position, I have written a directive (you can also find it here):

app.directive "setWindowScroll", ->
  restrict: "E"
  scope:
    setVariable: "="

  link: (scope) ->
    $(window).scroll ->

      scope.$apply ->
        scope.setVariable = $(window).scrollTop()

My problem is that this makes scrolling around in the table u*nsuably slow*. This is because the veriable I write to with my directive is in the extra-info in the scope, and changing that seems to cause angular to dirty-check the whole table for changes when my $apply is called.

I know that this scrolling will never change anything in my table, and would like to restrict my $apply to affect the header part of my site.

How can I make angular not dirty check the table?

like image 854
nh2 Avatar asked May 02 '13 09:05

nh2


1 Answers

Angular does the dirty checking under a process called digest. When you do a call to $scope.$digest() it will propagate to every child scope of $scope. To notify angular about changes you usually do a $scope.$apply(). At the end of the $apply() angular does a digest on the root scope, which triggers a diggest on every scope in your application. So to avoid the digest on in your scope with the big table scope, you should make sure that it is not the child of the extra information scope, and rather than doing an $scope.$apply() in your directive you could do a $scope.$digest(). This might be a bit confusing so I've made a plunker that tries to show the difference. Open your console and see the difference in the scroll event and button click:

http://plnkr.co/edit/45gKhAIt0DrozS7f0Bz2?p=preview

// this will only cause a digest in the current scope and its children
angular.element($window).bind('scroll',function(){
  $scope.scrollY = $window.scrollY;
  $scope.$digest();
})

// this will cause a digest in every scope
angular.element($window).bind('scroll',function(){
  $scope.scrollY = $window.scrollY;
  $scope.$apply();
})

When all this is said, it's a very unusual thing to do - and probably not a good idea for a number of reasons (angular doesnt scale well with thousands of elements, you can't use any of the angular event directives (ngClick etc) because they're all wrapped in $apply) - but if you can't render the table on the server side you can give this a try.

like image 146
joakimbl Avatar answered Nov 17 '22 22:11

joakimbl