2

I've read a lot of suggestions on how to improve performance of an ngRepeat but I couldn't still understand how to achieve my goal. I have this template:

<ul>
   <li ng-repeat="item in items">
     {{item.title}}
     <ul>
        <li ng-repeat="child in item.children">
          <a href="{{child.link}}">{{child.name}}</a>
          <some other heavy element that takes time to render>
        </li>
     </ul>
   </li>
 </ul>

What I would like to accomplish is a quick initial repeat that just shows child.name and then a separate non-blocking operation (directive, web worker, deferred magic, whatever) that handles the rendering of the heavy elements. In this way the GUI remains snappy and the <a> element is clickable quite immediately; meanwhile, in background, other stuff gets loaded in the DOM.

It this possible?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
stilllife
  • 1,776
  • 1
  • 18
  • 38
  • What exactly is in your 'heavy' element? – hally9k Oct 05 '15 at 09:37
  • It's just a small favicon image (which is also already cached in local storage) but still with roughly 500 "children" it takes 4 seconds to render and unfreeze the GUI – stilllife Oct 05 '15 at 11:00
  • can you post that heavy element template and part of data. or a jsfiddle? – YOU Oct 05 '15 at 11:04

2 Answers2

1

You can use a directive for that. The directive can do an asynchronous work (or even using a worker if you can do that) to deffer the displaying of its content.

But if you have to insert 100000 html tags at once in the DOM, you'll always have a freeze. It's not an Angular problem. Browsers just can't handle that.

Magus
  • 14,796
  • 3
  • 36
  • 51
  • I've tried but the DOM is not filled in until the directive has done is job and the GUI is frozen since the thread is still the same. I've tried also changing the digest cycle using `$timeout` in the directive, which kind of helps but the links are still not clickable until the repeat has fully terminated – stilllife Oct 05 '15 at 09:23
  • 2
    You can't avoid a "freeze" when you insert a "heavy amount of elements" in the DOM. For example if you insert 1000000 `
    ` at the same time, the browser will freeze. Maybe you need to think differently your user interface ?
    – Magus Oct 05 '15 at 09:51
  • Yes, I agree with this. I suggest using paging if you can. – trees_are_great Oct 05 '15 at 10:46
1

Rendering takes time, full stop. 'Infinite scroll' might be a useful search term for you if you are dealing with a high volume of content. Paging is another option. Either way you need to limit the amount of rendering you are asking the Dom to do at any one time.

Another approach would be to $compile your heavy content into the DOM after your initial ng-repeat has completed rendering by triggering an event on the ng-init of the ng-repeat and having a <heavy-content> directive listening for the event ready to compile its content into the DOM...

Here is the markup...

    <div ng-repeat="item in items" ng-init="ngRepeatReady()">
        <div>light content {{item}}</div>
        <heavy-content></heavy-content>
    </div>

... controller, notice the $timeout wrapping the $broadcast, this is important to avoid missing the last items in the ng-repeat...

app.controller('MyCtrl', function($scope, $timeout){

     $scope.items = [1,2,3,4,5,6];

     $scope.ngRepeatReady = function() {
         $timeout(function(){
            $scope.$broadcast('ngRepeatReady');
         });
     }

})    

... directive with a $timeout for the examples sake...

app.directive('heavyContent', function($timeout){
    return {
            template: '<p>loading...</p>',
            link: function(scope, element, attrs){
            scope.$on('ngRepeatReady', function(){
               $timeout(function(){
                  element.html('<h1>HEAVY CONTENT</h1>').show();
                  $compile(element.contents())(scope);
                  scope.$apply();
               }, 1000);
            });
        } 
    }
});

... and a working code pen.

hally9k
  • 2,423
  • 2
  • 25
  • 47
  • Adding one more point, always use one-time binding where-ever possible in the expressions. Here, for example, you can use it for the "a" tags. – SaiGiridhar Oct 05 '15 at 10:45
  • so there is no way to reduce the amount of rendering splitting each repeat in multiple-pass ? the only way is infinite scroll ? – stilllife Oct 05 '15 at 11:06
  • Yes you can compile elements into the Dom after the initial render. What would be your trigger? $viewContentLoaded ? That could work, I'll do a fiddle when I get to my machine... – hally9k Oct 05 '15 at 18:23
  • @hally9k thanks for the suggested approach, I've modified a little the pen to show my problem. The deferred render works fine but it still freezes the GUI. See in my example with only 200 items the links are not clickable and the scroll doesn't work until the entire render is done http://codepen.io/stilllife00/pen/JYNWpR – stilllife Oct 06 '15 at 09:02