AngularJS无限$digest循环,即使没有作用域改变

11

在我的Angular代码中,我遇到了以下错误。我很难理解为什么getDrawWithResults函数会引起一次digest周期,因为似乎没有任何副作用?它只是从一个设置为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;
    });
}
2个回答

14

我假设每次运行_.filter都会返回一个新的数组实例。这会引起Angular的隐式$watch,如下所示:

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

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

考虑到模型已经改变,因此需要重新消化。

我会实现一个过滤器

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

并像下面一样应用它(参见下面的编辑):

ng-hide="selectedLottery.draws | withResults"

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

在评论讨论后修改

实际问题是这个绑定:

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

ng-hide 注册了一个监视器,该监视器将一直触发,因为过滤数组的引用始终在变化。可以将其更改为:

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

以及对应的过滤器:

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

ng-repeat没有同样的问题,因为它注册了一个$watchCollection


这似乎仍然导致相同的问题? 错误:[$rootScope:infdig]达到10个$digest()迭代。中止! 在过去的5次迭代中触发的监视器:[["selectedLottery.draws|withResults; newVal: []; oldVal: []"],["selectedLottery.draws|withResults; newVal: []; oldVal: []"],["selectedLottery.draws|withResults; newVal: []; oldVal: []"],["selectedLottery.draws|withResults; newVal: []; oldVal: []"],["selectedLottery.draws|withResults; newVal: []; oldVal: []"]] - foiseworth
在这里查看:http://plnkr.co/edit/xEOe6Pg0akjsHiPrUySL?p=preview。然后,注释掉前两个绘图,并查看ng-hide也起作用了。正如预期的那样,每次绑定时都会调用withResults过滤器2次。你能否创建一个类似的plunker来重现这个问题? - Kos Prov
请查看此版本,它使用了getDrawsWithResults(您的原始代码)。显然,它也可以工作,所以我假设新数组会混淆Angular的digest是不正确的。我认为问题出在其他地方。也许是selectedLotterryselectedLotterry.draws在digest期间发生了变化。 - Kos Prov
我最终通过编写一个新函数返回布尔值来修复它。我认为我也可以通过执行!!getDrawsWithResults(selectedLottery.draws).length 来进行修复,这也将返回一个布尔值。 - foiseworth
1
好的...现在我看到了错误。ng-repeatselectedLottery.draws数组上注册了一个$scope.$watchCollection。这意味着它监视它的元素而不是实际引用。另一方面,ng-hide注册了一个$scope.$watch并接受一些通过一些启发式测试(称为toBoolean的私有函数)评估为true的东西。每次数组引用更改时,该观察将触发,这总是导致摘要失败。我还将编辑我的答案。 - Kos Prov
@KosProv 请注意,代码中有两个问题:1)在 app.filter('withResults' 后面缺少逗号。2)代码块末尾缺少一个闭合括号。但是 S.O. 不允许仅编辑两个字符。 - James Cazzetta

7
这意味着$scope.getDrawsWithResults()不是幂等的。给定相同的输入和状态,它不能始终返回相同的结果。而ng-hide需要一个幂等函数(所有Angular有监视的函数都是幂等函数)。
简而言之,你可能最好使用返回单个布尔值结果的函数,而不是返回数组的_.filter。也许可以使用_.all
这里幂等性很重要,因为Angular的$digest循环工作方式。因为有了你的ng-hide,Angular会在你的$scope.getDrawsWithResults()结果上放置一个监视器。这样,每当它重新评估那个ng-hide时,就会被提醒。你的ng-repeat没有受到影响,因为它的结果不需要被Angular监视。
所以,每次发生$digest(每秒发生很多次)时,都会调用$scope.getDrawsWithResults()来查看它的结果是否与先前的$digest周期不同,从而确定它是否应该更改ng-hide。如果结果已经改变,Angular知道这也可能意味着它正在监视的某些其他函数(可能使用来自您的函数的结果)也可能已经改变。因此,它需要重新运行$digest(让更改在必要时传播到系统中)。
因此,$digest会一直运行,直到它所监视的所有函数的结果停止变化。或者直到已经进行了10个$digest周期。Angular假定如果系统在10个周期后仍然不稳定,它可能永远无法稳定下来。因此,它放弃并抛出您得到的错误消息。
如果您愿意,可以在此深入了解所有内容:http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html

你是说它不具有幂等性,因为它每次都不返回相同的数组?即使数组具有相同的内容。这对我来说很有道理。我会给Kos答案,但只是因为他提出了如何解决这个问题的建议。 - foiseworth
1
如果认为 @KayakDave 应该得到答案,因为他发现问题特别出在 ng-hide 上面。我之前认为问题通常是将绑定到返回不同值的函数调用的概念上(这是我学习时非常难以避免的事情)。 - Kos Prov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接