ng-repeatのある関数によって返されたアイテムをループする方法は?


114

divを繰り返し作成したいのですが、アイテムは関数によって返されるオブジェクトです。ただし、次のコードはエラーを報告します。$ digest()の反復が10回に達しました。中止します!jsfiddleはこちら:http ://jsfiddle.net/BraveOstrich/awnqm/

<body ng-app>
  <div ng-repeat="entity in getEntities()">
    Hello {{entity.id}}!
  </div>
</body>

回答:


195

短い答え:本当にそのような機能が必要ですか、それともプロパティを使用できますか?http://jsfiddle.net/awnqm/1/

長い答え

簡単にするために、オブジェクトの配列のngRepeatについてのみ説明します。また、詳細は省略します。

AngularJSはダーティチェックを使用して変更を検出します。アプリケーションが起動すると、それが実行さ$digest$rootScopeます。スコープの階層の$digest深さ優先トラバーサルを行います。すべてのスコープに時計のリストがあります。各時計には最後の値があります(最初はinitWatchVal)。すべてのウォッチのスコープごとに$digest実行し、現在の値(watch.get(scope))を取得してと比較しwatch.lastます。現在の値がwatch.last(常に最初の比較で)等しくない場合は、に$digest設定さdirtytrueます。からすべてのスコープが処理される場合、dirty == true $digestから別の深さ優先トラバーサルが開始され$rootScopeます。$digestダーティ== falseまたはトラバーサルの数== 10で終了します。後者の場合、エラー「10 $ digest()反復に達しました」。ログに記録されます。

今についてngRepeatwatch.get呼び出しごとに、コレクションからのオブジェクト(の値を返すgetEntities)と追加情報を(HashQueueMapによってhashKey)キャッシュに保存します。すべてのwatch.get呼び出しで、キャッシュからngRepeatオブジェクトを取得しようとしますhashKey。それは、キャッシュに存在しない場合はngRepeat新しいスコープを作成し、プットがそれにオブジェクトをキャッシュに格納し、DOM要素、作成など

今についてhashKey。通常hashKeyはによって生成される一意の番号nextUid()です。しかし、それは機能することができますhashKey将来の使用のために生成した後、オブジェクトに格納されます。

例でエラーが発生する理由:関数はgetEntities()常に新しいオブジェクトの配列を返します。このオブジェクトはキャッシュにhashKey存在せず、ngRepeatキャッシュにも存在しません。したがってngRepeat、それぞれwatch.getがの新しいウォッチを使用して、そのスコープを生成し{{entity.id}}ます。この時計は最初にwatch.get持っていwatch.last == initWatchValます。ですからwatch.get() != watch.last。だから、$digest新しいトラバースを開始します。だから、ngRepeat新しい時計を持つ新しいスコープを作成します。したがって、10回のトラバースの後、エラーが発生します。

それを修正する方法

  1. すべてのgetEntities()呼び出しで新しいオブジェクトを作成しないでください。
  2. 新しいオブジェクトを作成する必要がある場合はhashKey、それらのメソッドを追加できます。例については、このトピックを参照してください。

AngularJSの内部を知っている人が、私が何か間違っている場合は修正してくれるといいのですが。


4
+1ありがとうございます。同じ問題があり、静的プロパティを使用できませんでした。$$ hashKeyは、本当にIMOのマニュアルのngRepeatページに記載されているはずです。
Michael Moussa、2013

これに影響を与えた1.1.3から1.1.4への変更点は何ですか?1.1.4以前では、これは実際に機能していました。それに関する変更ログには何もありません、そして私は違いが何であるかを推論することはできません。現在の動作は理にかなっています。
m59 2014年

:あなたができればまた、このアウトをチェックstackoverflow.com/questions/20933261/...私の答えは...行くかではない方法であれば、私はよく分からない
M59

2
推奨に従ってのでDo not create new objects on every getEntities() call.:それはこのように、かなり簡単に固定することができます<div ng-repeat="entity in entities = (entities || getEntities())">
przno

2
私の以前のコメントの解決策は、getEntities()常に同じ配列を返す場合に機能します。配列が変更された場合、それはng-repeat
przno

44

繰り返しの外で配列を初期化する

<body ng-app>
   <div ng-init="entities = getEntities()">
       <div ng-repeat="entity in entities">
           Hello {{entity.id}}!
       </div>
   </div>
</body>

8
getEntities()プログラムのライフサイクルでが何か異なるものを返す場合、これは機能しません。たとえば、それがをgetEntities()トリガーするとし$http.getます。getが最終的に解決されたとき(AJAX呼び出しを行ったとき)は、entitiesすでに初期化されています。
Nighto

3
角度のあるドキュメントからThe only appropriate use of ngInit is for aliasing special properties of ngRepeat. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Blowsie 2015

主なポイントは、「リピートの外側で配列を初期化する」ことだと思います...そして、約束に対する@Nightoの良い呼びかけ
Mwayi

15

これはここ報告され、この応答を得ました:

ゲッターはべき等ではなく、モデルが変更されます(呼び出されるたびに新しい配列が生成されます)。これにより、angularはモデルが最終的に安定することを期待してそれを呼び出し続けますが、angularは決してあきらめず、例外をスローします。

ゲッターが返す値は同じですが同一ではなく、それが問題です。

アレイをメインコントローラの外に移動すると、この動作がなくなることがわかります。

var array = [{id:'angularjs'}];
function Main($scope) {
    $scope.getEntities = function(){return array;};
};

今は毎回同じオブジェクトを返すからです。関数ではなくスコープのプロパティを使用するには、モデルを再構築する必要がある場合があります。

コントローラーのメソッドの結果をプロパティに割り当て、それに対してng:repeatを実行することで回避しました。


関数が各反復で変化するパラメーターを持っている場合、プロパティを使用することが唯一の方法である可能性があります。
ステファン

7

@prznoコメントに基づく

<body ng-app>
  <div ng-repeat="item in t = angular.equals(t, getEntities()) ? t : getEntities()">
    Hello {{item.id}}!
  </div>
</body>

BTWの2番目のソリューション@Artem Andreevは、Angular 1.1.4以降では機能しないことを示唆していますが、最初のソリューションは問題を解決しません。だから、私は今のところ恐れていますが、これは機能に不利な点のないスパイクの少ないソリューションです


item.idですか?entity.idが意味する場合、説明していただけますか?どうもありがとう!
Gerfried

はい、その通りです。Item.idそれがすべきことですb。ありがとう
Agat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.