Saya menemukan bahwa saya perlu memperbarui halaman saya ke lingkup saya secara manual lebih dan lebih sejak membangun aplikasi di sudut.
Satu-satunya cara saya tahu untuk melakukan ini adalah menelepon $apply()
dari lingkup pengontrol dan arahan saya. Masalah dengan ini adalah bahwa ia terus melempar kesalahan ke konsol yang berbunyi:
Kesalahan: $ digest sudah dalam proses
Adakah yang tahu bagaimana cara menghindari kesalahan ini atau mencapai hal yang sama tetapi dengan cara yang berbeda?
angularjs
angularjs-scope
angularjs-digest
Bola lampu1
sumber
sumber
$timeout()
ng-*
). Pastikan, jika Anda menelepon dari dalam fungsi (yang disebut melalui batas waktu / ajax / peristiwa), bahwa itu tidak juga dijalankan pada beban awalnya.Jawaban:
Anda dapat memeriksa apakah a
$digest
sudah dalam proses dengan memeriksa$scope.$$phase
.$scope.$$phase
akan kembali"$digest"
atau"$apply"
jika a$digest
atau$apply
sedang berlangsung. Saya percaya perbedaan antara negara-negara ini adalah yang$digest
akan memproses jam tangan dari ruang lingkup saat ini dan anak-anaknya, dan$apply
akan memproses pengamat semua lingkup.Untuk titik @ dnc253, jika Anda menemukan diri Anda menelepon
$digest
atau$apply
sering, Anda mungkin salah melakukannya. Saya biasanya menemukan saya perlu mencerna ketika saya perlu memperbarui keadaan lingkup sebagai akibat dari peristiwa DOM yang menembak di luar jangkauan Angular. Misalnya, ketika modal bootstrap twitter menjadi tersembunyi. Terkadang acara DOM menyala ketika a$digest
sedang berlangsung, kadang tidak. Itu sebabnya saya menggunakan cek ini.Saya ingin tahu cara yang lebih baik jika ada yang tahu.
Dari komentar: oleh @anddoutoi
angular.js Anti Patterns
sumber
if (!$scope.$$phase) $scope.$apply()
", github.com/angular/angular.js/wiki/Anti-PatternsDari diskusi baru-baru ini dengan orang-orang Angular tentang topik ini: Untuk alasan pembuktian di masa depan, Anda sebaiknya tidak menggunakannya
$$phase
Ketika ditekan untuk cara "benar" untuk melakukannya, jawabannya saat ini
Baru-baru ini saya mengalami hal ini ketika menulis layanan sudut untuk membungkus facebook, google, dan API Twitter yang, pada berbagai tingkat, memiliki panggilan balik.
Berikut ini contoh dari dalam layanan. (Demi singkatnya, sisa layanan - yang mengatur variabel, menyuntikkan $ timeout dll - telah ditinggalkan.)
Perhatikan bahwa argumen penundaan untuk $ timeout adalah opsional dan akan menjadi 0 jika dibiarkan tidak disetel ( $ timeout memanggil $ browser.defer yang defaultnya ke 0 jika penundaan tidak diatur )
Sedikit tidak intuitif, tapi itu jawaban dari orang-orang yang menulis Angular, jadi itu cukup bagus untukku!
sumber
$timeout
daripada yang aslisetTimeout
, mengapa Anda tidak menggunakan$window
yang asliwindow
?$timeout
dalam kasus ini, adalah$timeout
memastikan bahwa cakupan sudut diperbarui dengan benar. Jika $ digest tidak dalam proses, itu akan menyebabkan $ digest baru dijalankan.cancel
itu. Dari dokumen : "Sebagai hasil dari ini, janji itu akan diselesaikan dengan penolakan." Anda tidak bisa menyelesaikan janji yang sudah ditentukan. Pembatalan Anda tidak akan menyebabkan kesalahan, tetapi juga tidak akan melakukan hal positif.Siklus digest adalah panggilan sinkron. Itu tidak akan menghasilkan kontrol ke loop acara browser sampai selesai. Ada beberapa cara untuk mengatasinya. Cara termudah untuk mengatasinya adalah dengan menggunakan built in $ timeout, dan cara kedua adalah jika Anda menggunakan garis bawah atau lodash (dan seharusnya), hubungi yang berikut ini:
atau jika Anda memiliki lodash:
Kami mencoba beberapa solusi, dan kami benci menyuntikkan $ rootScope ke semua pengontrol, arahan, dan bahkan beberapa pabrik kami. Jadi, batas waktu $ dan _.defer telah menjadi favorit kami sejauh ini. Metode-metode ini berhasil memberitahu angular untuk menunggu hingga loop animasi berikutnya, yang akan menjamin bahwa ruang lingkup saat ini. $ Apply sudah berakhir.
sumber
underscore.js
. Solusi ini tidak layak mengimpor seluruh pustaka garis bawah hanya untuk menggunakandefer
fungsinya. Saya lebih suka$timeout
solusinya karena semua orang sudah memiliki akses$timeout
melalui sudut, tanpa ada ketergantungan pada perpustakaan lain.Banyak jawaban di sini berisi saran yang bagus tetapi juga dapat menyebabkan kebingungan. Hanya menggunakan
$timeout
adalah tidak yang terbaik atau solusi yang tepat. Juga, pastikan untuk membaca bahwa jika Anda khawatir dengan penampilan atau skalabilitas.Hal-hal yang harus Anda ketahui
$$phase
bersifat pribadi untuk kerangka kerja dan ada alasan bagus untuk itu.$timeout(callback)
akan menunggu sampai siklus intisari saat ini (jika ada) selesai, kemudian jalankan callback, lalu jalankan di akhir penuh$apply
.$timeout(callback, delay, false)
akan melakukan hal yang sama (dengan penundaan opsional sebelum menjalankan panggilan balik), tetapi tidak akan memunculkan$apply
(argumen ketiga) yang menyimpan kinerja jika Anda tidak mengubah model Angular Anda ($ lingkup).$scope.$apply(callback)
memohon, antara lain,$rootScope.$digest
,, yang berarti itu akan mereduksi ruang lingkup root aplikasi dan semua anak-anaknya, bahkan jika Anda berada dalam ruang lingkup yang terisolasi.$scope.$digest()
hanya akan menyinkronkan modelnya ke tampilan, tetapi tidak akan mencerna ruang lingkup orang tuanya, yang dapat menyimpan banyak kinerja ketika bekerja pada bagian yang terisolasi dari HTML Anda dengan ruang lingkup yang terisolasi (sebagian besar dari direktif). $ digest tidak menerima panggilan balik: Anda mengeksekusi kode, lalu mencernanya.$scope.$evalAsync(callback)
telah diperkenalkan dengan angularjs 1.2, dan mungkin akan menyelesaikan sebagian besar masalah Anda. Silakan merujuk paragraf terakhir untuk mempelajari lebih lanjut tentang hal itu.jika Anda mendapatkannya
$digest already in progress error
, maka arsitektur Anda salah: Anda tidak perlu memperbaiki ruang lingkup Anda, atau Anda tidak harus bertanggung jawab atas itu (lihat di bawah).Cara menyusun kode Anda
Ketika Anda mendapatkan kesalahan itu, Anda mencoba untuk mencerna ruang lingkup Anda saat ini sedang dalam proses: karena Anda tidak tahu keadaan ruang lingkup Anda pada saat itu, Anda tidak bertanggung jawab menangani pencernaannya.
Dan jika Anda tahu apa yang Anda lakukan dan bekerja pada arahan kecil yang terisolasi sementara bagian dari aplikasi Angular besar, Anda bisa lebih suka $ digest daripada lebih dari $ berlaku untuk menyimpan kinerja.
Pembaruan sejak Angularjs 1.2
Metode baru yang kuat telah ditambahkan ke $ scope apa saja:
$evalAsync
. Pada dasarnya, ia akan menjalankan callbacknya dalam siklus digest saat ini jika ada, jika tidak, siklus digest baru akan mulai mengeksekusi callback.Itu masih tidak sebagus
$scope.$digest
jika Anda benar-benar tahu bahwa Anda hanya perlu menyinkronkan bagian yang terisolasi dari HTML Anda (karena yang baru$apply
akan dipicu jika tidak ada yang sedang berlangsung), tetapi ini adalah solusi terbaik ketika Anda menjalankan fungsi yang Anda tidak dapat mengetahuinya apakah akan dijalankan secara sinkron atau tidak , misalnya setelah mengambil sumber daya yang berpotensi di-cache: kadang-kadang ini membutuhkan panggilan async ke server, jika tidak sumber daya akan diambil secara lokal secara sinkron.Dalam kasus ini dan semua yang lainnya di mana Anda memiliki
!$scope.$$phase
, pastikan untuk menggunakannya$scope.$evalAsync( callback )
sumber
$timeout
dikritik secara sepintas. Bisakah Anda memberi lebih banyak alasan untuk dihindari$timeout
?Metode pembantu kecil yang berguna untuk menjaga proses ini KERING:
sumber
scope.$apply(fn);
seharusnyascope.$apply(fn());
karena fn () akan menjalankan fungsi dan bukan fn. Tolong bantu saya ke tempat saya salahSaya memiliki masalah yang sama dengan skrip pihak ketiga seperti CodeMirror misalnya dan Krpano, dan bahkan menggunakan metode safeApply yang disebutkan di sini belum memecahkan kesalahan bagi saya.
Tapi yang harus diselesaikan adalah menggunakan layanan $ timeout (jangan lupa menyuntikkannya terlebih dahulu)
Jadi, sesuatu seperti:
dan jika di dalam kode Anda yang Anda gunakan
mungkin karena itu di dalam pengontrol direktif pabrik atau hanya perlu semacam ikatan, maka Anda akan melakukan sesuatu seperti:
sumber
Lihat http://docs.angularjs.org/error/$rootScope:inprog
Masalah muncul ketika Anda memiliki panggilan ke
$apply
yang kadang-kadang dijalankan secara asinkron di luar kode Angular (ketika $ berlaku harus digunakan) dan kadang-kadang sinkron dalam kode Angular (yang menyebabkan$digest already in progress
kesalahan).Ini dapat terjadi, misalnya, ketika Anda memiliki perpustakaan yang secara tidak sinkron mengambil item dari server dan menyimpannya. Pertama kali suatu item diminta, itu akan diambil secara tidak sinkron agar tidak memblokir eksekusi kode. Namun, kedua kalinya item tersebut sudah ada dalam cache sehingga dapat diambil secara serempak.
Cara untuk mencegah kesalahan ini adalah dengan memastikan bahwa kode yang dipanggil
$apply
dijalankan secara tidak sinkron. Ini dapat dilakukan dengan menjalankan kode Anda di dalam panggilan$timeout
dengan penundaan diatur ke0
(yang merupakan default). Namun, memanggil kode Anda di dalam$timeout
menghilangkan keharusan untuk menelepon$apply
, karena $ timeout akan memicu$digest
siklus lain sendiri, yang pada gilirannya akan melakukan semua pembaruan yang diperlukan, dll.Larutan
Singkatnya, alih-alih melakukan ini:
melakukan hal ini:
Hanya panggilan
$apply
bila Anda tahu kode yang menjalankannya akan selalu dijalankan di luar kode Angular (mis. Panggilan Anda ke $ apply akan terjadi di dalam callback yang dipanggil oleh kode di luar kode Angular Anda).Kecuali jika seseorang menyadari adanya kerugian yang merugikan
$timeout
akibat penggunaan yang berlebihan$apply
, saya tidak melihat mengapa Anda tidak selalu dapat menggunakan$timeout
(dengan nol penundaan)$apply
, karena hal itu akan melakukan hal yang kira-kira sama.sumber
$apply
diri saya tetapi masih mendapatkan kesalahan.$apply
sinkron (callback dieksekusi, maka kode berikut $ berlaku) sementara$timeout
tidak: kode saat ini setelah batas waktu dieksekusi, maka tumpukan baru dimulai dengan callbacknya, seolah-olah Anda sedang menggunakansetTimeout
. Itu dapat menyebabkan gangguan grafis jika Anda memperbarui dua kali model yang sama:$timeout
akan menunggu tampilan menjadi segar sebelum memperbaruinya lagi.Ketika Anda mendapatkan kesalahan ini, itu pada dasarnya berarti sudah dalam proses memperbarui tampilan Anda. Anda benar-benar tidak perlu memanggil
$apply()
dalam controller Anda. Jika tampilan Anda tidak diperbarui seperti yang Anda harapkan, dan kemudian Anda mendapatkan kesalahan ini setelah menelepon$apply()
, kemungkinan besar Anda tidak memperbarui model dengan benar. Jika Anda memposting beberapa hal spesifik, kami dapat menemukan masalah intinya.sumber
you're not updating the the model correctly
?$scope.err_message = 'err message';
apakah pembaruan tidak benar?$apply()
adalah ketika Anda memperbarui model "luar" sudut (misalnya dari plugin jQuery). Sangat mudah untuk jatuh ke dalam jebakan pandangan yang tidak terlihat benar, dan jadi Anda melempar banyak$apply()
di mana-mana, yang kemudian berakhir dengan kesalahan yang terlihat di OP. Ketika saya mengatakanyou're not updating the the model correctly
maksud saya semua logika bisnis tidak mengisi dengan benar apa pun yang mungkin ada dalam ruang lingkup, yang mengarah ke tampilan yang tidak tampak seperti yang diharapkan.Bentuk aman terpendek
$apply
adalah:sumber
Anda juga dapat menggunakan evalAsync. Ini akan berjalan beberapa saat setelah digest selesai!
sumber
Pertama-tama, jangan perbaiki dengan cara ini
Itu tidak masuk akal karena $ phase hanyalah flag boolean untuk siklus $ digest, jadi $ apply Anda () terkadang tidak akan berjalan. Dan ingat itu praktik yang buruk.
Sebaliknya, gunakan
$timeout
Jika Anda menggunakan garis bawah atau lodash, Anda dapat menggunakan defer ():
sumber
Terkadang Anda masih akan mendapatkan kesalahan jika Anda menggunakan cara ini ( https://stackoverflow.com/a/12859093/801426 ).
Coba ini:
sumber
$rootScope
dananyScope.$root
orang yang sama.$rootScope.$root
berlebihan.Anda harus menggunakan $ evalAsync atau $ timeout sesuai dengan konteksnya.
Ini adalah tautan dengan penjelasan yang bagus:
sumber
coba gunakan
dari pada
$ applyAsync Jadwalkan permohonan $ apply untuk terjadi di lain waktu. Ini dapat digunakan untuk mengantri banyak ekspresi yang perlu dievaluasi dalam intisari yang sama.
CATATAN: Di dalam $ digest, $ applyAsync () hanya akan memerah jika lingkup saat ini adalah $ rootScope. Ini berarti bahwa jika Anda memanggil $ digest pada lingkup anak, itu tidak akan secara otomatis memunculkan antrian $ applyAsync ().
Exmaple:
Referensi:
1. Cakupan. $ ApplyAsync () vs. Cakupan. $ EvalAsync () di AngularJS 1.3
sumber
Saya akan menyarankan Anda untuk menggunakan acara khusus daripada memicu siklus intisari.
Saya datang untuk mengetahui bahwa menyiarkan acara khusus dan mendaftarkan pendengar untuk acara ini adalah solusi yang baik untuk memicu tindakan yang Anda inginkan terjadi baik saat Anda sedang dalam siklus intisari maupun tidak.
Dengan membuat acara khusus, Anda juga menjadi lebih efisien dengan kode Anda karena Anda hanya memicu pendengar yang berlangganan acara tersebut dan TIDAK memicu semua jam tangan yang terikat pada ruang lingkup seperti yang Anda lakukan jika Anda menggunakan ruang lingkup. $ Terapkan.
sumber
yearofmoo melakukan pekerjaan yang hebat dalam menciptakan fungsi $ safe yang dapat digunakan kembali untuk kita:
Penggunaan:
sumber
Saya sudah bisa menyelesaikan masalah ini dengan menelepon
$eval
alih-alih$apply
di tempat-tempat di mana saya tahu bahwa$digest
fungsinya akan berjalan.Menurut dokumen ,
$apply
pada dasarnya melakukan ini:Dalam kasus saya, suatu
ng-click
perubahan variabel dalam lingkup, dan $ menonton pada variabel itu mengubah variabel lain yang harus$applied
. Langkah terakhir ini menyebabkan kesalahan "intisari sudah dalam proses".Dengan mengganti
$apply
dengan$eval
di dalam ekspresi arloji, variabel ruang lingkup diperbarui seperti yang diharapkan.Oleh karena itu, tampaknya jika intisari akan berjalan karena beberapa perubahan lain dalam Angular,
$eval
itulah yang perlu Anda lakukan.sumber
gunakan
$scope.$$phase || $scope.$apply();
sajasumber
Memahami bahwa dokumen sudut panggilan memeriksa
$$phase
sebuah anti-pola , saya mencoba untuk mendapatkan$timeout
dan_.defer
bekerja.Metode batas waktu dan yang ditangguhkan membuat flash
{{myVar}}
konten yang tidak diparsing dalam dom seperti FOUT . Bagi saya ini tidak dapat diterima. Itu membuat saya tanpa banyak diberitahu dogmatis bahwa ada sesuatu yang hack, dan tidak memiliki alternatif yang cocok.Satu-satunya hal yang berfungsi setiap saat adalah:
if(scope.$$phase !== '$digest'){ scope.$digest() }
.Saya tidak mengerti bahaya dari metode ini, atau mengapa ini digambarkan sebagai peretasan oleh orang-orang di komentar dan tim sudut. Perintah itu tampaknya tepat dan mudah dibaca:
Dalam CoffeeScript itu bahkan lebih cantik:
scope.$digest() unless scope.$$phase is '$digest'
Apa masalahnya dengan ini? Apakah ada alternatif yang tidak akan membuat FOUT? $ safeApply terlihat bagus tetapi menggunakan
$$phase
metode inspeksi juga.sumber
Ini adalah layanan utilitas saya:
dan ini adalah contoh untuk penggunaannya:
sumber
Saya telah menggunakan metode ini dan tampaknya berfungsi dengan baik. Ini hanya menunggu waktu siklus selesai dan kemudian memicu
apply()
. Cukup panggil fungsiapply(<your scope>)
dari mana saja Anda inginkan.sumber
Ketika saya menonaktifkan debugger, kesalahan tidak terjadi lagi. Dalam kasus saya , itu karena debugger menghentikan eksekusi kode.
sumber
mirip dengan jawaban di atas tetapi ini telah bekerja dengan setia untuk saya ... dalam sebuah layanan tambahkan:
sumber
Kamu bisa menggunakan
untuk mencegah kesalahan.
sumber
Masalah ini pada dasarnya datang ketika, kami meminta sudut untuk menjalankan siklus intisari meskipun dalam proses yang menciptakan masalah untuk sudut ke pemahaman. pengecualian konsekuensi di konsol.
1. Tidak masuk akal untuk memanggil ruang lingkup. $ Apply () di dalam fungsi $ timeout karena secara internal ia melakukan hal yang sama.
2. Kode berjalan dengan fungsi vanilla JavaScript karena asalnya tidak angular angular didefinisikan yaitu setTimeout
3. Untuk melakukan itu Anda dapat menggunakan
if (! Scope. $$ phase) {
scope. $ EvalAsync (function () {
}); }
sumber
Ini solusi yang bagus untuk menghindari kesalahan ini dan menghindari $ apply
Anda dapat menggabungkan ini dengan debounce (0) jika memanggil berdasarkan peristiwa eksternal. Di atas adalah 'debounce' yang kami gunakan, dan contoh lengkap kode
dan kode itu sendiri untuk mendengarkan suatu peristiwa dan memanggil $ digest hanya pada $ scope yang Anda butuhkan
sumber
Ditemukan ini: https://coderwall.com/p/ngisma di mana Nathan Walker (dekat bagian bawah halaman) menyarankan seorang dekorator dalam $ rootScope untuk membuat func 'safeApply', kode:
sumber
Ini akan menyelesaikan masalah Anda:
sumber