8

I've tried many different things to fix performance issues of ng-repeat. including stuff described here: How to 'unwatch' an expression

I need to have a large set of rows on the page up to ~1000 rows. With every row containing quite a bit of stuff. And it seems to me now, it's just would be very slow with ng-repeat, I guess I have to build either my own custom ng-repeat or I have to build a directive that will build every single row in the table... I don't know how to do either. Can you guys help me please. Can you show me some examples.

Community
  • 1
  • 1
iLemming
  • 34,477
  • 60
  • 195
  • 309
  • 1
    @blesh provided an example directive in the link you mentioned. Try that and let us know what's not working. – Mark Rajcok Jan 31 '13 at 00:07
  • check out my answer below I created a set-repeat that iterates through a set of data one time and does not add watch listeners to the page. I use it in my app in order to overcome memory issues as well. – btm1 Oct 13 '13 at 08:54

3 Answers3

11

Here is an example of populating a <dl> with <dt>s and <dd>s ...

Step 01 - create a widge.product.details.js

// binds to $scope.details = [] //array object

angular.module('widget.product.details',[])
  .directive('productDetails',function(){
   return {
    template:'<dl class="dl-horizontal"></dl>',
    replace:true,
    restrict:'E',
    compile : function compile(tElement, tAttrs, transclude) {
     return {
      post: createProductDetails
     }
    }
   }
  });

var createProductDetails = function (scope, iElement, iAttrs, controller) {
    scope.$watch('details', function(newVal, oldVal) {
    angular.forEach(newVal, function(v,k){
        iElement.append( angular.element('<dt>'+v.dt+'</dt><dd>'+v.dd+'</dd>') );
        });
    });
}

Step 02 - create your html

<div class="span7" ng-controller="ProductInfoCtrl">
 <product-details></product-details>
</div>

Step 03 - create a app.product.js

function ProductInfoCtrl($scope) {
 $scope.details = [
                   {dt:'condition',dd:'brand new'},
                   {dt:'year bought',dd:'3 years ago'},
                   ]
}
sss
  • 1,259
  • 9
  • 23
  • 2
    With your `forEach()` loop wouldn't it be better for performance, to store it all in a string then do one `append()` after the `forEach()`? – Pete Nov 17 '13 at 21:22
  • maybe, sounds like a good idea. i would trust a benchmark... :D – sss Dec 05 '13 at 09:39
  • FYI: Using compile { post: .. } is the same as using link: function(scope, element, attributes) – Daniel Macias Feb 07 '15 at 23:00
5
angular.module('setRepeat',[]).directive('setRepeat', function () {

  return {
    transclude: 'element',
    priority: 1000,
    compile: compileFun
  };

  function compileFun(element, attrs, linker) {
      var expression = attrs.setRepeat.split(' in ');
      expression = {
        child : expression[0],
        property : expression[1]
      };

      return {
        post: repeat
      };

      function repeat(scope, iele, iattrs /*, attr*/) {
        var template = element[0].outerHTML;
        var data = scope.$eval(expression.property);
        addElements(data,scope,iele);

        return;

        function makeNewScope (index, expression, value, scope, collection) {
          var childScope = scope.$new();
          childScope[expression] = value;
          childScope.$index = index;
          childScope.$first = (index === 0);
          childScope.$last = (index === (collection.length - 1));
          childScope.$middle = !(childScope.$first || childScope.$last);

          /**
          *
          * uncomment this if you want your children to keep listening for changes
          *
          **/

          //childScope.$watch(function updateChildScopeItem(){
            //childScope[expression] = value;
          //});
          return childScope;
        }

        function addElements (collection, scope, insPoint) {
          var frag = document.createDocumentFragment();
          var newElements = [], element, idx, childScope;

          angular.forEach(data, function(v,i){
            childScope = makeNewScope(i,expression.child,v,scope,collection);
            element = linker(childScope, angular.noop);
            newElements.push(element);
            frag.appendChild(element[0]);
          });

          insPoint.after(frag);
          return newElements;
        }
      }
  }

});
btm1
  • 3,866
  • 2
  • 23
  • 26
  • Hi, how do you set a watch for the setRepeat expression? I mean how to update children when the `property` changes? – supersan Feb 11 '16 at 12:42
  • 1
    @supersan thats the whole point of this that it doesn't change... so only use this on stuff that doesn't need to be updated. Also FYI the angular team has finally listed and created a bind once syntax so you don't really need this anymore. You can now do ng-repeat="item in ::list" and it will bind the repeat once and stop listening – btm1 Feb 11 '16 at 20:58
  • oh okay.. you're right! Actually I stumbled upon this question from Google and I was looking for a way to create a custom ng-repeat (for use in my own datatables like directive). Anyway, thanks for the clarification. – supersan Feb 12 '16 at 07:47
0

Simple code for custom ngReapeat directive in angularJS :

   <!DOCTYPE html>
   <html>
    <head>
       <script type='text/javascript' src='angular.min.js'></script>    
    </head>
    <body ng-app="customNgReapeat">
      <div ng-controller='ProductInfoCtrl'>
        <div custom-repeat></div>
      </div>    
    </body>
  </html>

JS Code

    var csREapeat = angular.module('customNgReapeat', [])
      .directive('customRepeat', function() {
        return {
          restrict: 'A',
          link: function(scope, iElement, iAttrs, controller) {
            angular.forEach(scope.details, function(v, k) {
              iElement.append(angular.element('<div>' + v.name + '</div>             <div>' + v.address + '</div>'));
            });
          }
        };
      })

 function ProductInfoCtrl($scope) {
   $scope.details = [
      {name: 'Nidhi', address: 'India-Gurgaon'},
      {name: 'Khushi', address: 'India-Ghazipur'}
   ];
}
Kara
  • 6,115
  • 16
  • 50
  • 57
suryadev
  • 107
  • 1
  • 3