【问题标题】:Array.sort angular filter and "Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!"Array.sort 角度过滤器和“错误:[$rootScope:infdig] 10 $digest() 迭代已达到。中止!”
【发布时间】:2015-05-03 14:52:02
【问题描述】:

我正在尝试对 ng-repeat 指令中的一些元素进行排序。

我创建了一个自定义过滤器,尽管我收到错误消息,但它似乎可以工作:

“错误:[$rootScope:infdig] 达到 10 个 $digest() 迭代。正在中止!”

从我读到的其他答案来看,这通常是由于每个过滤器返回不同的值。我已经检查过了,我返回的是同一个数组对象……所以我不确定是什么原因造成的。

.filter('sortOffers', function() {
        return function(vals, predicate, reverse) {
            var vals_old = vals;

            vals.sort(function(a, b) {
                var sorter = reverse ? 1 : -1;

                if(predicate === 'seeking') {
                    if(a.data.valueSought > b.data.valueSought) {
                        return sorter;
                    } else if(a.data.valueSought < b.data.valueSought) {
                        return -sorter;
                    } else {
                        return 0;
                    }
                }

                if(predicate === 'offering') {
                    if(a.data.valueOffered > b.data.valueOffered) {
                        return sorter;
                    } else if(a.data.valueOffered < b.data.valueOffered) {
                        return -sorter;
                    } else {
                        return 0;
                    }
                }
            });

            return vals;
        }
    });

html:

        <tr ng-repeat="offer in offers | sortOffers:sortOffersPredicate:sortOffersReverse" ng-hide="hideDueDateSellers && offer.data.offerType === 'seeking_flexibility' || hideFlexibilitySellers && offer.data.offerType === 'seeking_duedate'">
            <td>{{ offer.data.valueOffered || '0' }} {{ offer.offeringString }}</td>
            <td>{{ offer.data.valueSought || '0' }} {{ offer.seekingString }}</td>
            <td ng-if="offer.data._owner !== currentUser._id"><button class="ui button positive" ng-click="acceptOffer(offer)">Accept Offer</button></td>
            <td ng-if="offer.data._owner === currentUser._id"><button class="ui button negative" ng-click="cancelOffer(offer)">Cancel Your Offer</button></td>
        </tr>

【问题讨论】:

  • 您对过滤器的用途感到困惑。过滤器不能以任何方式修改数据。数据的任何修改都会导致摘要,这会导致过滤器被评估,数据被修改(再次),摘要运行(再次),没有结束。过滤器只能用于限制返回的结果,不能修改数据。在应用任何过滤器之前,应在控制器中对数据进行排序。
  • 所以改变数组的顺序是什么导致了问题?
  • 如果是这种情况,那么为什么要按过滤器排序? docs.angularjs.org/api/ng/filter/orderBy
  • 我正在尝试研究原生 orderBy 过滤器的代码,看看他们是如何解决这个问题的……我之前没有理由深入研究它。

标签: javascript angularjs


【解决方案1】:

所以这里的问题是基于数组在过滤器中被修改的事实。但是,有一种解决方法,如原生 orderBy 过滤器的原始源代码所示。

数组本身正在被.sort() 函数修改,触发$digest 循环。使用.slice()会创建一个可以排序的新数组,新数组可以在不触发$digest的情况下返回。

return vals.slice().sort(function(a, b) {

这里有更多上下文...

返回与原始数据结构相同的不同数组并没有错。事实上,如果要消除项目,您必须返回与原始数组不同的数组。令人困惑的是,您无法返回 values 已更改的数组(原始或副本)。通过使用.slice(),返回的数组只被评估一次作为.sort()的结果,并且原始数组永远不会改变。

但是,接受一个数组并使用它来评估数据并返回具有完全不同结构的不同数组的过滤器将不起作用。

【讨论】:

  • 完美,谢谢,不过我有点困惑,因为我在其他问题中读到这就是你会得到这样一个错误的确切原因(因为你应该返回相同的数组而不是新的一)
  • 我试过这种方式,但数组没有正确排序。可能是因为我的特殊情况。我不知道
【解决方案2】:

在进行排序之前复制数组。就地修改数组是触发 $watch 的原因:

        return vals.slice().sort(function(a, b) {
            var sorter = reverse ? 1 : -1;

            if(predicate === 'seeking') {
                if(a.data.valueSought > b.data.valueSought) {
                    return sorter;
                } else if(a.data.valueSought < b.data.valueSought) {
                    return -sorter;
                } else {
                    return 0;
                }
            }

            if(predicate === 'offering') {
                if(a.data.valueOffered > b.data.valueOffered) {
                    return sorter;
                } else if(a.data.valueOffered < b.data.valueOffered) {
                    return -sorter;
                } else {
                    return 0;
                }
            }
        });

我相信 ng-repeat $watches 使用 $watchCollection 使用右侧 (rhs) 表达式进行更改。 $watchCollection 不按引用比较数组,而是查找长度差异,并且会逐项比较数组。

【讨论】: