web-dev-qa-db-ja.com

スコープが変更されていない場合の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;
    });
}
11
foiseworth

_.filterは、実行されるたびに新しい配列インスタンスを返すと思います。これにより、Angularの暗黙の$watchesは次のようになります。

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

そしてそれをそのように適用します(以下の編集を参照):

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を登録するため、同じ問題は発生しません。

14
Kos Prov

これは、$scope.getDrawsWithResults()がべき等ではないことを意味します。同じ入力と状態が与えられた場合、一貫して同じ結果が返されるわけではありません。そして、ng-hideにはべき等関数が必要です(Angularが監視しているすべての関数と同様)。

つまり、配列を返す_.filterの代わりに、単一のブール結果を返す関数を使用したほうがよい場合があります。おそらく_.all

ここでべき等性が重要である理由は、Angularの$ digestサイクルが機能する方法のためです。 ng-hide Angularは、$scope.getDrawsWithResults()の結果を監視します。こうすることで、ng-hideを再評価する必要があるときにアラートを受け取ることができます。ng-repeatは影響を受けません。結果をAngularで監視する必要はありません。

したがって、$ダイジェストが発生するたびに(1秒間に何回も)$scope.getDrawsWithResults()が呼び出され、結果が前の$ダイジェストサイクルから変更されたかどうか、したがってng-hideを変更する必要があるかどうかが確認されます。結果が変更された場合Angularは、監視している他の関数(関数の結果を使用している可能性があります)が変更された可能性があることを認識しています。したがって、$ digestを再実行する必要があります(必要に応じて、変更はシステム全体に伝播します)。

そのため、$ digestは、監視しているすべての関数の結果が変化しなくなるまで実行を続けます。または、$ digestサイクルが10回になるまで。 Angularは、システムが10サイクル後に安定しない場合、おそらく安定しないと想定しているため、あきらめて、受け取ったエラーメッセージをスローします。

必要に応じて、ここでこれをさらに詳しく調べることができます: http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-および-digest.html

7
KayakDave