Ng-tekrarlı bir işlev tarafından döndürülen öğeler arasında nasıl döngü yapılır?


114

Tekrar tekrar div oluşturmak istiyorum, öğeler bir işlev tarafından döndürülen nesnelerdir. Ancak aşağıdaki kod hataları bildiriyor: 10 $ Digest () yinelemelerine ulaşıldı. Durduruluyor! jsfiddle burada: http://jsfiddle.net/BraveOstrich/awnqm/

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

Yanıtlar:


195

Kısa cevap : Gerçekten böyle bir işleve ihtiyacınız var mı yoksa özelliği kullanabilir misiniz? http://jsfiddle.net/awnqm/1/

Uzun cevap

Basit olması için sadece sizin durumunuzu açıklayacağım - nesne dizisi için ngRepeat. Ayrıca bazı ayrıntıları atlayacağım.

AngularJS, değişiklikleri tespit etmek için kirli kontrol kullanır . Uygulama başlatıldığında o ishal $digestiçin $rootScope. kapsam hiyerarşisi$digest için önce derinlik geçişi yapacak . Tüm kapsamlarda saat listesi vardır. Her saatin son değeri vardır (başlangıçta ). Tüm saatler için her kapsam için onu çalıştırır, mevcut değeri ( ) alır ve ile karşılaştırır . Geçerli değer değilse için eşit (ilk karşılaştırmak her zaman için) setleri için . Tüm kapsamlar işlendiğinde, başka bir derinlik-ilk geçişi başlatırsa . dirty == false veya geçiş sayısı == 10. olduğunda sona erer. İkinci durumda, "10 $ özet () yinelemeye ulaşıldı" hatası. günlüğe kaydedilecek.initWatchVal$digestwatch.get(scope)watch.lastwatch.last$digestdirtytruedirty == true $digest$rootScope$digest

Şimdi hakkında ngRepeat. Her watch.getçağrı için, koleksiyondaki nesneleri (dönen değeri getEntities) önbellekte ( HashQueueMaptarafından hashKey) ek bilgilerle birlikte depolar . Her watch.getçağrı ngRepeatiçin, nesneyi hashKeyönbellekten almaya çalışır . Önbellekte yoksa, önbellekte ngRepeatdepolar, yeni kapsam oluşturur, üzerine nesne koyar, DOM öğesi oluşturur vb .

Şimdi hakkında hashKey. Genellikle hashKeytarafından üretilen benzersiz sayıdır nextUid(). Ancak işlev olabilir . hashKeygelecekteki kullanım için oluşturulduktan sonra nesnede saklanır.

Örneğiniz neden hata oluşturuyor : işlev getEntities()her zaman yeni nesneyle dizi döndürür. Bu nesne önbellekte yok hashKeyve yok ngRepeat. Bu yüzden ngRepeather biri watch.getyeni bir saat ile onun için yeni kapsam oluşturur {{entity.id}}. Bu saat ilk önce watch.getvar watch.last == initWatchVal. Yani watch.get() != watch.last. Böylece $digestyeni geçiş başlar. Böylece ngRepeatyeni saatle yeni kapsam yaratır. Yani ... 10 geçişten sonra hata alırsınız.

Nasıl düzeltebilirsin

  1. Her getEntities()aramada yeni nesneler oluşturmayın .
  2. Yeni nesneler oluşturmanız gerekiyorsa, hashKeyonlar için yöntem ekleyebilirsiniz . Örnekler için bu konuya bakın .

Umarım AngularJS iç bileşenlerini bilen insanlar, bir konuda yanıldıysam beni düzeltir.


4
+1 bunun için teşekkür ederim. Aynı sorunu yaşadım ve bunun için statik bir özellik kullanamadım. $$ hashKey, manuel IMO'nun ngRepeat sayfasında gerçekten belgelenmelidir.
Michael Moussa

Bunu etkileyen 1.1.3'ten 1.1.4'e neyin değiştiğine dair bir fikriniz var mı? 1.1.4'ten önce bu gerçekten çalıştı. Değişiklik günlüğünde bununla ilgili hiçbir şey yok ve farkın ne olduğunu anlayamıyorum. Mevcut davranış mantıklı.
m59

Ayrıca, yapabiliyorsanız şunu da kontrol edin: stackoverflow.com/questions/20933261/… Cevabımın gidilecek yol olup olmadığından emin değilim ..
m59

2
Bu nedenle, tavsiyenin ardından şu Do not create new objects on every getEntities() call.şekilde kolayca düzeltilebilir:<div ng-repeat="entity in entities = (entities || getEntities())">
przno

2
önceki getEntities()ng-repeat
yorumumdaki

44

Yinelemenin dışında diziyi başlat

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

8
Bu getEntities(), programın yaşam döngüsünde farklı bir şey döndürürse çalışmaz . Örneğin, getEntities()bir $http.get. Nihayet çözümlendiğinde (AJAX çağrısını yaptınız), entitieszaten başlatılmış olacaktır.
Nighto

3
Açısal belgelerdenThe 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

Bence asıl nokta "tekrarın dışında diziyi başlatmak" her ne şekilde olursa olsun ... ve @Nighto vaatler için iyi bir çağrı
Mwayi

15

Bu burada bildirildi ve şu yanıtı aldı:

Alıcınız idempotent değildir ve modeli değiştirir (her çağrıldığında yeni bir dizi oluşturarak). Bu, modelin sonunda stabilize olacağı umuduyla onu açısal olarak adlandırmaya devam etmeye zorluyor, ancak asla açısal olarak pes etmiyor ve bir istisna atıyor.

Alıcının getirdiği değerler eşittir ancak aynı değildir ve sorun budur.

Diziyi Ana denetleyicinin dışına taşırsanız bu davranışın kaybolduğunu görebilirsiniz:

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

çünkü şimdi her seferinde aynı nesneyi döndürüyor. Bir işlev yerine kapsamda bir özellik kullanmak için modelinizi yeniden yapılandırmanız gerekebilir:

Denetleyicinin yönteminin sonucunu bir özelliğe atayarak ve ona karşı ng: tekrar yaparak bunun etrafında çalıştık.


Bir özelliği kullanmak, işlevin her yinelemede değişen bir parametresi varsa tek yol olabilir.
Stephane

7

@Przno yorumuna göre

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

BTW ikinci çözüm @Artem Andreev, Angular 1.1.4 ve üzeri sürümlerde çalışmadığını öne sürerken, ilk çözüm sorunu çözmüyor. Öyleyse, korkarım şimdilik, işlevsellik açısından dezavantajları olmayan daha az dikenli bir çözüm


İtem.id mi demek istiyorsun? Eğer varlık.id demek istediğiniz ise, lütfen açıklar mısınız? Çok teşekkürler!
Gerfried

Evet sen haklisin. Item.idolması gereken şeydir b. Teşekkürler
Agat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.