Angularjs бесконечный цикл $digest, если изменения области не изменяются

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

Ошибка возникает только при первом использовании getDrawWithResults на странице, если я удаляю, ошибка останавливается.

Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["getDrawsWithResults(selectedLottery.draws); newVal: []; oldVal: []"],["getDrawsWithResults(selectedLottery.draws); newVal: []; oldVal: []"],["getDrawsWithResults(selectedLottery.draws); newVal: []; oldVal: []"],["getDrawsWithResults(selectedLottery.draws); newVal: []; oldVal: []"],["getDrawsWithResults(selectedLottery.draws); newVal: []; oldVal: []"]]

Это мой код:

HTML

<h4 ng-cloak ng-hide="getDrawsWithResults(selectedLottery.draws)">Results of Selected Lottery</h4>

<div class="con" ng-repeat="draw in getDrawsWithResults(selectedLottery.draws)" ng-cloak>
    <h5 class="con__header">[[ draw.date|date:'EEEE d MMMM yyyy - H:mm' ]]</h5>
    <div class="balls-list__outer con__row">
        <div class="balls-list">
            <div class="balls-list__ball__outer" ng-repeat="b in draw.results">
                <button class="balls-list__ball btn btn-con">[[ b ]]</button>
            </div>

        </div>
    </div>
</div>

JS

// return list of draws with results
$scope.getDrawsWithResults = function(draws) {
    return _.filter(draws, function(draw){
        return draw.results && draw.results.length > 0;
    });
}

Ответ 1

Я предполагаю, что _.filter возвращает новый экземпляр массива каждый раз при его запуске. Это вызывает angular неявный $watch es как:

ng-hide="getDrawsWithResults(selectedLottery.draws)"

и

ng-repeat="draw in getDrawsWithResults(selectedLottery.draws)"

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

Я бы выполнил filter

app.filter('withResults', function() {
    return function(draws) {
        return _.filter(draws, function(draw){
            return draw.results && draw.results.length > 0;
        });
    }
})

и примените его так (см. раздел EDIT ниже):

ng-hide="selectedLottery.draws | withResults"

и

ng-repeat="draw in selectedLottery.draws | withresults"

EDITED после обсуждения в комментариях

Актуальной проблемой является эта привязка:

ng-hide="getDrawsWithResults(selectedLottery.draws)"

ng-hide регистрирует часы, которые будут срабатывать вечно, так как ссылка фильтрованного массива всегда изменяется. Его можно изменить на:

ng-hide="getDrawsWithResults(selectedLottery.draws).length > 0"

и соответствующий фильтр:

ng-hide="(selectedLottery.draws | withResults).length > 0"

ng-repeat не имеет такой же проблемы, поскольку он регистрирует $watchCollection.

Ответ 2

Это означает, что $scope.getDrawsWithResults() не является идемпотентным. Учитывая тот же ввод и состояние, он не всегда возвращает тот же результат. И ng-hide требует идемпотентную функцию (как и вся функция, с которой Angular имеет часы).

Короче говоря, вам может быть лучше использовать функцию, которая возвращает единственный логический результат вместо _.filter, который возвращает массив. Возможно, _.all?

Причина идемпотенциала здесь связана с тем, что цикл Angular $digest работает. Из-за вашего ng-hide Angular помещает часы на результаты вашего $scope.getDrawsWithResults(). Таким образом, он может быть предупрежден, когда он должен переоценить это ng-hide. Ваш ng-repeat не влияет, потому что его результаты не нуждаются в просмотре Angular.

Таким образом, каждый раз, когда происходит переполнение $(это много раз в секунду) вызывается $scope.getDrawsWithResults(), чтобы увидеть, изменились ли результаты из предыдущего цикла $digest и, следовательно, следует ли это изменить ng-hide. Если результат изменился, Angular знает, что также может означать, что какая-то другая функция, которую он просматривает (которая, возможно, использует результат вашей функции), могла бы измениться. Таким образом, ему необходимо повторно запустить $digest (позволяя, если потребуется, распространение через систему).

И поэтому $digest продолжает работать до тех пор, пока результаты всех функций, которые он смотрит, перестанут меняться. Или до тех пор, пока не будет 10 $циклов дайджеста. Angular предполагает, что если система не стабильна после 10 циклов, она, вероятно, никогда не стабилизируется. И поэтому он отказывается и выдает сообщение об ошибке, которое вы получили.

Вы можете погрузиться в это все более подробно здесь, если хотите: http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html