Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to reduce the number of watchers in ng-repeat

In a performance purpose, i wanted to remove the double data-binding (so, the associated watchers) from my ng-repeat.

It loads 30 items, and those data are static once loaded, so no need for double data binding.

The thing is that the watcher amount remains the same on that page, no matter how i do it.

Let say :


<div ng-repeat='stuff in stuffs'>
   // nothing in here
</div>

Watchers amount is 211 (there is other binding on that page, not only ng-repeat)


<div ng-repeat='stuff in ::stuffs'>
   // nothing in here
</div>

Watchers amount is still 211 ( it should be 210 if i understand it right), but wait :


<div ng-repeat='stuff in ::stuffs'>
    {{stuff.id}}
</div>

Watchers amount is now 241 (well ok, 211 watchers + 30 stuffs * 1 watcher = 241 watchers)


<div ng-repeat='stuff in ::stuffs'>
    {{::stuff.id}}
</div>

Watchers amount is still 241 !!! Is :: not supposed to remove associated watcher ??


<div ng-repeat='stuff in ::stuffs'>
    {{stuff.id}}  {{stuff.name}}  {{stuff.desc}}
</div>

Still 241...


Those exemples has really been made in my app, so those numbers are real too.

The real ng-repeat is far more complex than the exemple one here, and i reach ~1500 watchers on my page. If i delete its content ( like in the exemple), i fall down to ~200 watchers. So how can i optimize it ? Why :: does't seem to work ?

Thank you to enlighten me...

like image 778
Nuzzob Avatar asked Jul 13 '16 15:07

Nuzzob


People also ask

What can I use instead of NG-repeat?

You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.

How do I get the index of an element in NG-repeat?

Note: The $index variable is used to get the Index of the Row created by ng-repeat directive. Each row of the HTML Table consists of a Button which has been assigned ng-click directive. The $index variable is passed as parameter to the GetRowIndex function.

Where is the last element in NG-repeat?

$first and $last It's common when using ng-repeat to add specific behavior to the first or last element of the loop, e.g. special styling around the edges. Instead, ng-repeat already supplies you with two ready boolean properties. $first is true for the first element, and $last is true for the last element.

What is the use of NG-repeat?

Angular-JS ng-repeat directive is a handy tool to repeat a set of HTML code for a number of times or once per item in a collection of items. ng-repeat is mostly used on arrays and objects.


1 Answers

It's hard to figure out what's the exact problem in your specific case, maybe it makes sense to provide an isolated example, so that other guys can help.

The result might depend on how you count the watchers. I took solution from here.

Here is a Plunker example working as expected (add or remove :: in ng-repeat):

HTML

<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script data-require="[email protected]" data-semver="1.5.6" src="https://code.angularjs.org/1.5.6/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>
  <body ng-controller="mainCtrl">
    <div>{{name}}</div>

    <ul>
      <li ng-repeat="item in ::items">{{::item.id}} - {{::item.name}}</li>
    </ul>

    <button id="watchersCountBtn">Show watchers count</button>
    <div id="watchersCountLog"></div>
  </body>
</html>

JavaScript

angular
  .module('app', [])
  .controller('mainCtrl', function($scope) {
    $scope.name = 'Hello World';

    $scope.items = [
      { id: 1, name: 'product 1' },
      { id: 2, name: 'product 2' },
      { id: 3, name: 'product 3' },
      { id: 4, name: 'product 4' },
      { id: 5, name: 'product 5' },
      { id: 6, name: 'product 6' },
      { id: 7, name: 'product 7' },
      { id: 8, name: 'product 8' },
      { id: 9, name: 'product 9' },
      { id: 10, name: 'product 10' }
    ];
  });

function getWatchers(root) {
  root = angular.element(root || document.documentElement);
  var watcherCount = 0;

  function getElemWatchers(element) {
    var isolateWatchers = getWatchersFromScope(element.data().$isolateScope);
    var scopeWatchers = getWatchersFromScope(element.data().$scope);
    var watchers = scopeWatchers.concat(isolateWatchers);
    angular.forEach(element.children(), function (childElement) {
      watchers = watchers.concat(getElemWatchers(angular.element(childElement)));
    });
    return watchers;
  }

  function getWatchersFromScope(scope) {
    if (scope) {
      return scope.$$watchers || [];
    } else {
      return [];
    }
  }

  return getElemWatchers(root);
}

window.onload = function() {
  var btn = document.getElementById('watchersCountBtn');
  var log = document.getElementById('watchersCountLog');

  window.addEventListener('click', function() {
    log.innerText = getWatchers().length;
  });
};

Hope this helps.

like image 135
sbedulin Avatar answered Oct 18 '22 21:10

sbedulin