Search code examples
angularjsangularjs-digest

Infinite Digest Loop in AngularJS filter


I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error. Why does this occur and how can I correct this?

angular.module("app", []).
filter('department', function(filterFilter) {
  return function(items, args) {
    var productMatches;
    var output = [];
    var count = 0;

    if (args.selectedDepartment.Id !== undefined && args.option) {
      for (let i = 0; i < items.length; i++) {

        productMatches = items[i].products.filter(function(el) {
          return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
        });

        if (productMatches.length !== 0) {
          output[count] = {};
          output[count].products = productMatches;
          output[count].firstProduct = items[i].firstProduct;
          count++;
        }

      }
    }
    return output;
  };
}).

This is the relevant HTML:

<tr class='destination' ng-repeat-start='pickupAccount in pickupAccounts | department : {"selectedDepartment": selectedDepartment, "option": displayExclusive }'>
  <!-- td here -->
</tr>

displayExclusive is boolean.


Solution

  • I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error.

    Keep in mind that filter should return array of the same object structure. When we activate filter, it fires digest cycle that will run over our filter again. If something changed in output list - fires new digest cycle and so on. after 10 attempts it will throw us Infinite Digest Loop Exception


    Testing

    This empty filter will works (100%). Actually we do nothing here but return the same object that filter receives.

    filter('department', function(filterFilter) {
      return function(items, args) {
    
        var output = items;
    
        return output;
      };
    })
    

    Now the main idea is: write some condition to push to output objects from input list a.e. items based on some if statement, a.e.

    var output = [];
    
    if (args.selectedDepartment.Id !== undefined && args.option) {
       angular.forEach(items, function(item) {
           if(<SOME CONDITION>) {
              output.push(item);
            }            
        });
    }
    

    By this way it will work too.

    our case:

    we have this logic:

    productMatches = items[i].products.filter(function(el) {
          return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
        });
    
        if (productMatches.length !== 0) {
          output[count] = {};
          output[count].products = productMatches;
          output[count].firstProduct = items[i].firstProduct;
          count++;
        }
    

    Here we completely modified object that has been stored in output. So next digest cycle our items will change again and again.


    Conclusion

    The main purpose of filter is to filter list and not modify list object content.

    Above mentioned logic you wrote is related to data manipulation and not filter. The department filter returns the same length of items.

    To achieve your goal, you can use lodash map or underscorejs map for example.