Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$digest iteration error for filters written for ng-repeat in angular 1.1.x

I've been looking into trying to write filters but its been quite frustrating.

Here are a couple of resources I was following for writing a 'chunk' filter

https://groups.google.com/forum/#!topic/angular/IEIQok-YkpU https://groups.google.com/forum/#!topic/angular/gEv1-YV-Ojg

I tried it out with some success. but found that there is difference in behaviour between versions

The methods described to produce a $$hashKey does not work in the 1.1.5 version. The first fiddle is fine, whilst the second produces in iteration error, even though the code is exactly the same:

http://jsfiddle.net/nRGTX/38/ - 1.0.3 version

http://jsfiddle.net/nRGTX/39/ - 1.1.5 version

 Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: $watchCollectionWatch; newVal: 16; oldVal: 14"],["fn: $watchCollectionWatch; newVal: 18; oldVal: 16"],["fn: $watchCollectionWatch; newVal: 20; oldVal: 18"],["fn: $watchCollectionWatch; newVal: 22; oldVal: 20"],["fn: $watchCollectionWatch; newVal: 24; oldVal: 22"]]

Are there any workarounds/proper way to write this?

like image 802
zcaudate Avatar asked Jul 10 '13 04:07

zcaudate


1 Answers

Starting with version 1.1.4 the ng-repeat directive has a watch on the collection that you are iterating to make sure it hasn't changed. The way it works is that it compares every item in the array and if the items aren't equal (in the === sense), it thinks the collection has been updated. At least this is my understanding from having looked at the code.

If you were using a filter in the typical sense, where you are just returning a subset of the original items, then the items returned would be the same every time. However, because you're building a whole new structure, the items in the array are different every time (even though their content is the same), so the watch thinks the collection is constantly changing.

The only solution I could come up with was to create a cache of previously returned results. Every time the chunk filter is called, it checks to see if you've previously performed a filter with the same array and chunkSize. If so, it returns the cached result.

To do this, you should update your filter function to look something like this:

return function(array, chunkSize) {
  if (!(array instanceof Array)) return array;
  if (!chunkSize) return array;

  // Create empty cache if it doesn't already exist.  
  if (typeof myApp.chunkCache === "undefined") {
    myApp.chunkCache = [];
  }

  // Search the cache to see if we filtered the given array before.
  for (var i = 0; i < myApp.chunkCache.length; i++) {
    var cache = myApp.chunkCache[i];
    if (cache.array == array && cache.chunkSize == chunkSize) {
      return cache.result;
    }
  }

  // If not, construct the chunked result.
  var result = chunkArray(array, chunkSize);
  defineHashKeys(result);

  // And then add that result to the cache.
  var cache = {
    array: array,
    chunkSize: chunkSize,
    result: result
  };
  myApp.chunkCache.push(cache);

  return result;
}

I should also point out that you can probably remove the defineHashKeys call, because I don't think it serves any purpose in this code. I only left it in because it was in your original code. It didn't seem to make any difference when I removed it.

like image 169
James Holderness Avatar answered Jan 04 '23 23:01

James Holderness