Bagaimana cara melakukan Loop melalui item yang dikembalikan oleh fungsi dengan ng-repeat?

114

Saya ingin membuat div berulang kali, item adalah objek yang dikembalikan oleh suatu fungsi. Namun kesalahan laporan kode berikut: 10 $ digest () iterasi tercapai. Membatalkan! jsfiddle ada di sini: http://jsfiddle.net/BraveOstrich/awnqm/

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

Jawaban:

195

Jawaban singkatnya : apakah Anda benar-benar membutuhkan fungsi seperti itu atau Anda dapat menggunakan properti? http://jsfiddle.net/awnqm/1/

Jawaban panjang

Untuk kesederhanaan saya hanya akan menjelaskan kasus Anda - ngRepeat untuk berbagai objek. Juga, saya akan menghilangkan beberapa detail.

AngularJS menggunakan pemeriksaan kotor untuk mendeteksi perubahan. Ketika aplikasi dijalankan berjalan $digestuntuk $rootScope. $digestakan melakukan penjelajahan kedalaman pertama untuk hierarki cakupan . Semua cakupan memiliki daftar jam tangan. Setiap jam tangan memiliki nilai terakhir (awalnya initWatchVal). Untuk setiap cakupan untuk semua jam tangan $digestmenjalankannya, mendapatkan nilai saat ini ( watch.get(scope)) dan membandingkannya watch.last. Jika nilai saat ini tidak sama dengan watch.last(selalu untuk perbandingan pertama) $digestset dirtyke true. Saat semua cakupan diproses jika dirty == true $digestmemulai traversal kedalaman pertama lainnya dari $rootScope. $digestberakhir ketika dirty == false atau jumlah traversals == 10. Dalam kasus terakhir, error "10 $ digest () iterations tercapai." akan dicatat.

Sekarang tentang ngRepeat. Untuk setiap watch.getpanggilan itu menyimpan objek dari koleksi (nilai yang dikembalikan getEntities) dengan informasi tambahan dalam cache ( HashQueueMapoleh hashKey). Untuk setiap watch.getpanggilan ngRepeatmencoba untuk mendapatkan objek hashKeydari cache-nya. Jika tidak ada di cache, ngRepeatsimpan di cache, membuat cakupan baru, meletakkan objek di atasnya, membuat elemen DOM, dll .

Sekarang tentang hashKey. Biasanya hashKeynomor unik dihasilkan oleh nextUid(). Tapi itu bisa berfungsi . hashKeydisimpan dalam objek setelah dibuat untuk digunakan di masa mendatang.

Mengapa contoh Anda menghasilkan kesalahan : fungsi getEntities()selalu mengembalikan array dengan objek baru. Objek ini tidak memiliki hashKeydan tidak ada di ngRepeatcache. Jadi ngRepeatpada masing-masing watch.getmenghasilkan ruang lingkup baru untuk itu dengan jam tangan baru {{entity.id}}. Jam tangan ini pertama kali watch.getpunya watch.last == initWatchVal. Jadi watch.get() != watch.last. Jadi mulailah $digestlintasan baru. Jadi ngRepeatbuat ruang lingkup baru dengan jam tangan baru. Jadi ... setelah 10 traverse Anda mendapatkan error.

Bagaimana Anda bisa memperbaikinya

  1. Jangan membuat objek baru di setiap getEntities()panggilan.
  2. Jika Anda perlu membuat objek baru, Anda dapat menambahkan hashKeymetode untuk mereka. Lihat topik ini sebagai contoh.

Semoga orang yang mengetahui internal AngularJS akan mengoreksi saya jika saya salah dalam sesuatu.

Artem Andreev
sumber
4
+1 terima kasih untuk ini. Saya memiliki masalah yang sama dan tidak dapat menggunakan properti statis untuk itu. $$ hashKey harus benar-benar didokumentasikan di halaman ngRepeat dari IMO manual.
Michael Moussa
Adakah ide apa yang berubah dari 1.1.3 menjadi 1.1.4 yang mempengaruhi ini? Sebelum 1.1.4 ini benar-benar berfungsi. Tidak ada apa pun di log perubahan tentang hal itu dan saya tidak tahu apa perbedaannya. Perilaku saat ini masuk akal.
m59
Juga, periksa ini jika Anda bisa: stackoverflow.com/questions/20933261/… Saya tidak yakin apakah jawaban saya sesuai atau tidak ..
m59
2
Jadi mengikuti rekomendasi untuk Do not create new objects on every getEntities() call.itu dapat diperbaiki dengan mudah seperti ini:<div ng-repeat="entity in entities = (entities || getEntities())">
przno
2
solusi dari komentar saya sebelumnya berfungsi jika getEntities()selalu mengembalikan array yang sama, jika array pernah berubah, Anda tidak akan mendapatkannya ding-repeat
przno
44

Inisialisasi larik di luar pengulangan

<body ng-app>
   <div ng-init="entities = getEntities()">
       <div ng-repeat="entity in entities">
           Hello {{entity.id}}!
       </div>
   </div>
</body>
Mwayi
sumber
8
Ini tidak bekerja jika getEntities()mengembalikan sesuatu yang berbeda dalam siklus hidup program. Katakan, misalnya, getEntities()pemicu itu $http.get. Ketika get akhirnya menyelesaikan (Anda membuat panggilan AJAX), entitiesakan sudah diinisialisasi.
Nighto tanggal
3
Dari dokumen sudutThe 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
Saya pikir poin utamanya adalah untuk "menginisialisasi array di luar pengulangan" dengan cara apa pun ... dan @Nighto panggilan yang baik pada janji
Mwayi
15

Ini dilaporkan di sini dan mendapat tanggapan ini:

Getter Anda tidak idempoten dan mengubah model (dengan membuat array baru setiap kali dipanggil). Ini memaksa angular untuk terus memanggilnya dengan harapan bahwa model pada akhirnya akan stabil, tetapi tidak pernah begitu angular menyerah dan membuat pengecualian.

Nilai yang dikembalikan pengambil sama tetapi tidak identik dan itulah masalahnya.

Anda dapat melihat perilaku ini hilang jika Anda memindahkan array di luar pengontrol Utama:

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

karena sekarang mengembalikan objek yang sama setiap saat. Anda mungkin perlu merancang ulang model Anda untuk menggunakan properti pada scope alih-alih fungsi:

Kami mengatasinya dengan menetapkan hasil dari metode pengontrol ke properti, dan melakukan ng: repeat terhadapnya.

Dennis
sumber
Menggunakan properti mungkin satu-satunya cara jika fungsi memiliki parameter yang berubah pada setiap iterasi.
Stephane
7

Berdasarkan komentar @przno

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

Solusi kedua BTW @Artem Andreev menyarankan tidak berfungsi di Angular 1.1.4 dan yang lebih baru, sedangkan solusi pertama tidak menyelesaikan masalah. Jadi, saya khawatir untuk saat ini ini adalah solusi yang kurang tajam tanpa kerugian dalam fungsionalitas

Agat
sumber
Maksud Anda item.id? Jika entity.id yang Anda maksud, dapatkah Anda jelaskan? Terimakasih banyak!
Gerfried
Ya, Anda benar. Item.idadalah apa yang seharusnya b. Terima kasih
Agat