Бесконечный цикл дайджеста в фильтре AngularJS


Я написал этот пользовательский фильтр для AngularJS, но когда он запускается, я получаю бесконечную ошибку цикла дайджеста. Почему это происходит и как я могу это исправить?

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;
  };
}).

Это соответствующий HTML:

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

displayExclusive является логическим.

3 18

3 ответа:

Я написал этот пользовательский фильтр для AngularJS, но когда он запускается, я получаю бесконечную ошибку цикла дайджеста.

Имейте в виду, что фильтр должен возвращать массив той же структуры объекта. Когда мы активируем фильтр, он запускает цикл дайджеста, который снова будет работать над нашим фильтром. Если что - то изменилось в списке вывода-запускает новый цикл дайджеста и так далее. после 10 попыток он бросит нам Infinite Digest Loop исключение


Тестирование

Этот пустой фильтр будет работать (100%). На самом деле мы здесь ничего не делаем, а возвращаем тот же самый объект, который получает фильтр.

filter('department', function(filterFilter) {
  return function(items, args) {

    var output = items;

    return output;
  };
})

Теперь основная идея такова: напишите некоторое условие, чтобы подтолкнуть к output объектам из входного списка a. e. items на основе некоторого if оператора, a. e.

var output = [];

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

Таким образом, это тоже будет работать.

Наш случай:

У нас есть такая логика:

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++;
    }

Здесь мы полностью модифицировали объект, который был сохранен в output. Так что в следующем цикле дайджеста наш items снова изменится и снова.


Заключение

Основная цель filter - фильтровать список, а не изменять содержимое объекта списка.

Вышеупомянутая логика, которую вы написали, связана с манипуляцией данными,а не с фильтром. Фильтр department возвращает одинаковую длину элементов.

Для достижения своей цели вы можете использовать карту lodash или карту underscorejs, например.

Это происходит, когда вы манипулируете возвращаемым массивом таким образом, что он не соответствует исходному массиву. См., например:

.filter("department", function() {
    return function(items, args) {
        var output = [];

        for (var i = 0; i < items.length; i++) {
            output[i] = {};
            output[i] = items[i]; // if you don't do this, the next filter will fail
            output[i].product = items[i];
        }
        return output;
    }
}

Вы можете увидеть, как это происходит в следующем упрощенном jsfiddle: https://jsfiddle.net/u873kevp/1/

Если возвращаемый массив имеет ту же "структуру", что и входной массив, это вызовет эти ошибки.

Это должно работать в вашем случае, просто назначив исходный элемент возвращаемому элементу:

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

output[count] = {};

Выше строка является основной проблемой. Вы создаете новый экземпляр, и ng-repeat обнаружит, что модель постоянно изменяется бесконечно. (в то время как вы думаете, что ничего не меняется с точки зрения пользовательского интерфейса)

Чтобы избежать этой проблемы, в основном вам нужно убедиться, что каждый элемент в модели остается "тем же самым", т. е.

firstCallOutput[0] == secondCallOutput[0]
&& firstCallOutput[1] == secondCallOutput[1]
&& firstCallOutput[2] == secondCallOutput[2]
...
Это равенство должно сохраняться до тех пор, пока вы не измените модель, таким образом, ng-repeat не будет "ошибочно" думать, что модель имеет все изменилось. Обратите внимание, что два новых экземпляра не равны, т. е. {} != {}