Ich stelle fest, dass ich meine Seite mehr und mehr manuell auf meinen Bereich aktualisieren muss, seit ich eine Anwendung in Winkel erstellt habe.
Die einzige Möglichkeit, dies zu tun, besteht darin, $apply()
aus dem Bereich meiner Controller und Anweisungen heraus aufzurufen . Das Problem dabei ist, dass immer wieder ein Fehler an die Konsole ausgegeben wird, der lautet:
Fehler: $ Digest wird bereits ausgeführt
Weiß jemand, wie man diesen Fehler vermeidet oder dasselbe erreicht, aber auf andere Weise?
angularjs
angularjs-scope
angularjs-digest
Glühbirne1
quelle
quelle
$timeout()
ng-*
) angewendet . Stellen Sie sicher, dass es nicht auch beim Laden ausgeführt wird , wenn Sie es innerhalb einer Funktion aufrufen (die über timeout / ajax / events aufgerufen wird) .Antworten:
Sie können überprüfen, ob a
$digest
bereits ausgeführt wird, indem Sie überprüfen$scope.$$phase
.$scope.$$phase
wird zurückkehren"$digest"
oder"$apply"
wenn ein$digest
oder$apply
in Bearbeitung ist. Ich glaube, der Unterschied zwischen diesen Zuständen besteht darin, dass$digest
die Uhren des aktuellen Bereichs und seiner Kinder verarbeitet werden und$apply
die Beobachter aller Bereiche verarbeitet werden.Bis zu @ dnc253 können Sie es falsch machen , wenn Sie anrufen
$digest
oder$apply
häufig sind. Im Allgemeinen muss ich verdauen, wenn ich den Status des Bereichs aufgrund eines DOM-Ereignisses aktualisieren muss, das außerhalb der Reichweite von Angular ausgelöst wird. Zum Beispiel, wenn ein Twitter-Bootstrap-Modal ausgeblendet wird. Manchmal wird das DOM-Ereignis ausgelöst, wenn a ausgeführt$digest
wird, manchmal nicht. Deshalb benutze ich diesen Check.Ich würde gerne einen besseren Weg kennen, wenn jemand einen kennt.
Aus Kommentaren: von @anddoutoi
angle.js Anti Patterns
quelle
if (!$scope.$$phase) $scope.$apply()
", github.com/angular/angular.js/wiki/Anti-PatternsAus einer kürzlichen Diskussion mit den Angular-Leuten zu diesem Thema: Aus zukunftssicheren Gründen sollten Sie nicht verwenden
$$phase
Wenn Sie auf die "richtige" Vorgehensweise klicken, lautet die Antwort derzeit
Ich bin kürzlich darauf gestoßen, als ich eckige Dienste schrieb, um die Facebook-, Google- und Twitter-APIs zu verpacken, bei denen in unterschiedlichem Maße Rückrufe eingereicht wurden.
Hier ist ein Beispiel aus einem Dienst. (Der Kürze halber wurde der Rest des Dienstes - der Variablen einrichtete, $ timeout usw. injizierte - weggelassen.)
Beachten Sie, dass die Verzögerung Argument für $ timeout ist optional und wird auf 0 standardmäßig , wenn nicht konfiguriert ( $ timeout ruft $ browser.defer , die standardmäßig auf 0 , wenn Verzögerung nicht gesetzt ist )
Ein bisschen nicht intuitiv, aber das ist die Antwort der Leute, die Angular schreiben, also ist es gut genug für mich!
quelle
$timeout
native verwendensetTimeout
, warum verwenden Sie nicht$window
anstelle von nativewindow
?$timeout
diesem Fall wird$timeout
sichergestellt, dass der Winkelbereich ordnungsgemäß aktualisiert wird. Wenn kein $ Digest ausgeführt wird, wird ein neuer $ Digest ausgeführt.cancel
dafür. Aus den Dokumenten : "Infolgedessen wird das Versprechen mit einer Ablehnung gelöst." Sie können ein gelöstes Versprechen nicht lösen. Ihre Stornierung wird keine Fehler verursachen, aber auch nichts Positives bewirken.Der Digest-Zyklus ist ein synchroner Aufruf. Die Ereignisschleife des Browsers wird erst dann gesteuert, wenn sie abgeschlossen ist. Es gibt einige Möglichkeiten, damit umzugehen. Der einfachste Weg, um damit umzugehen, ist die Verwendung des integrierten $ timeout. Ein zweiter Weg ist, wenn Sie Unterstrich oder lodash verwenden (und Sie sollten es sein), rufen Sie Folgendes an:
oder wenn Sie lodash haben:
Wir haben verschiedene Problemumgehungen ausprobiert und es gehasst, $ rootScope in alle unsere Controller, Direktiven und sogar einige Fabriken zu injizieren. Das $ timeout und _.defer waren bisher unser Favorit. Diese Methoden weisen Angular erfolgreich an, bis zur nächsten Animationsschleife zu warten, wodurch sichergestellt wird, dass der aktuelle Gültigkeitsbereich. $ Apply abgelaufen ist.
quelle
underscore.js
. Diese Lösung ist es nicht wert, die gesamte Unterstrichbibliothek zu importieren, nur um ihredefer
Funktion zu nutzen . Ich bevorzuge die$timeout
Lösung sehr, da jeder bereits über Angular Zugriff hat$timeout
, ohne von anderen Bibliotheken abhängig zu sein.Viele der Antworten hier enthalten gute Ratschläge, können aber auch zu Verwirrung führen. Einfach zu verwenden
$timeout
ist weder die beste noch die richtige Lösung. Lesen Sie dies auch, wenn Sie sich Gedanken über Leistung oder Skalierbarkeit machen.Dinge, die Sie wissen sollten
$$phase
ist privat für das Framework und es gibt gute Gründe dafür.$timeout(callback)
wird warten, bis der aktuelle Digest-Zyklus (falls vorhanden) abgeschlossen ist, dann den Rückruf ausführen und am Ende einen vollständigen ausführen$apply
.$timeout(callback, delay, false)
wird dasselbe tun (mit einer optionalen Verzögerung vor dem Ausführen des Rückrufs), aber kein$apply
(drittes Argument) auslösen, das Leistungen spart, wenn Sie Ihr Angular-Modell nicht geändert haben ($ scope).$scope.$apply(callback)
Ruft unter anderem auf,$rootScope.$digest
was bedeutet, dass der Stammbereich der Anwendung und alle untergeordneten Elemente neu definiert werden, selbst wenn Sie sich in einem isolierten Bereich befinden.$scope.$digest()
synchronisiert einfach das Modell mit der Ansicht, verdaut jedoch nicht den übergeordneten Bereich, wodurch eine Menge Leistung eingespart werden kann, wenn an einem isolierten Teil Ihres HTML-Codes mit einem isolierten Bereich gearbeitet wird (meistens aus einer Direktive). $ Digest nimmt keinen Rückruf entgegen: Sie führen den Code aus und dann Digest.$scope.$evalAsync(callback)
wurde mit anglejs 1.2 eingeführt und wird wahrscheinlich die meisten Ihrer Probleme lösen. Bitte lesen Sie den letzten Absatz, um mehr darüber zu erfahren.Wenn Sie das erhalten
$digest already in progress error
, ist Ihre Architektur falsch: Entweder müssen Sie Ihren Bereich nicht neu definieren, oder Sie sollten nicht dafür verantwortlich sein (siehe unten).So strukturieren Sie Ihren Code
Wenn Sie diesen Fehler erhalten, versuchen Sie, Ihren Bereich zu verdauen, während er bereits ausgeführt wird: Da Sie den Status Ihres Bereichs zu diesem Zeitpunkt nicht kennen, sind Sie nicht für die Verarbeitung verantwortlich.
Und wenn Sie wissen, was Sie tun und an einer isolierten kleinen Direktive arbeiten, während Sie Teil einer großen Angular-Anwendung sind, können Sie $ Digest anstelle von $ Apply vorziehen, um Leistungen zu sparen.
Update seit Angularjs 1.2
Jedem $ scope wurde eine neue, leistungsstarke Methode hinzugefügt:
$evalAsync
. Grundsätzlich führt es seinen Rückruf innerhalb des aktuellen Digest-Zyklus aus, wenn einer auftritt, andernfalls beginnt ein neuer Digest-Zyklus mit der Ausführung des Rückrufs.Das ist immer noch nicht so gut wie
$scope.$digest
wenn Sie wirklich wissen, dass Sie nur einen isolierten Teil Ihres HTML-Codes synchronisieren müssen (da ein neuer$apply
Teil ausgelöst wird, wenn keiner ausgeführt wird), aber dies ist die beste Lösung, wenn Sie eine Funktion ausführen die Sie können es nicht wissen , ob wird synchron oder nicht ausgeführt werden , möglicherweise im Cache gespeichert , nachdem das Abrufen eine Ressource, zum Beispiel: manchmal wird dies einen asynchronen Aufruf an einen Server benötigen, da sonst die Ressource lokal synchron abgerufen werden.!$scope.$$phase
Verwenden Sie in diesen und allen anderen Fällen, in denen Sie eine hatten , diese unbedingt$scope.$evalAsync( callback )
quelle
$timeout
wird im Vorbeigehen kritisiert. Können Sie weitere Gründe angeben, die Sie vermeiden sollten$timeout
?Praktische kleine Hilfsmethode, um diesen Prozess trocken zu halten:
quelle
scope.$apply(fn);
sollte sein,scope.$apply(fn());
weil fn () die Funktion ausführen wird und nicht fn. Bitte helfen Sie mir, wo ich falschIch hatte das gleiche Problem mit Skripten von Drittanbietern wie CodeMirror und Krpano, und selbst die Verwendung der hier genannten safeApply-Methoden hat den Fehler für mich nicht behoben.
Aber was es gelöst hat, ist die Verwendung des $ timeout-Dienstes (vergessen Sie nicht, ihn zuerst zu injizieren).
So etwas wie:
und wenn in Ihrem Code, den Sie verwenden
Vielleicht, weil es sich im Controller einer Factory-Direktive befindet oder nur eine Art Bindung benötigt, würden Sie etwas tun wie:
quelle
Siehe http://docs.angularjs.org/error/$rootScope:inprog
Das Problem tritt auf, wenn Sie einen Aufruf haben
$apply
, der manchmal asynchron außerhalb von Angular-Code ausgeführt wird (wenn $ apply verwendet werden sollte) und manchmal synchron innerhalb von Angular-Code (was das verursacht)$digest already in progress
Fehler verursacht).Dies kann beispielsweise passieren, wenn Sie über eine Bibliothek verfügen, die Elemente asynchron von einem Server abruft und zwischenspeichert. Wenn ein Element zum ersten Mal angefordert wird, wird es asynchron abgerufen, um die Codeausführung nicht zu blockieren. Beim zweiten Mal befindet sich das Element jedoch bereits im Cache, sodass es synchron abgerufen werden kann.
Um diesen Fehler zu vermeiden, müssen Sie sicherstellen, dass der aufgerufene Code
$apply
asynchron ausgeführt wird. Dies kann erreicht werden, indem Sie Ihren Code in einem Aufruf von$timeout
mit der Verzögerung0
(die Standardeinstellung) ausführen . Durch das Aufrufen Ihres Codes im Inneren$timeout
entfällt jedoch die Notwendigkeit eines Aufrufs$apply
, da $ timeout selbst einen weiteren$digest
Zyklus auslöst , der wiederum alle erforderlichen Aktualisierungen usw. durchführt.Lösung
Kurz gesagt, anstatt dies zu tun:
mach das:
Rufen
$apply
Sie nur auf, wenn Sie wissen, dass der Code, der ausgeführt wird, immer außerhalb des Angular-Codes ausgeführt wird (z. B. erfolgt Ihr Aufruf von $ apply innerhalb eines Rückrufs, der von Code außerhalb Ihres Angular-Codes aufgerufen wird).Ich verstehe nicht, warum Sie nicht immer (ohne Verzögerung) verwenden können , es sei denn, jemand ist sich eines schwerwiegenden Nachteils bei der Verwendung von
$timeout
over bewusst , da dies ungefähr das Gleiche bewirkt.$apply
$timeout
$apply
quelle
$apply
selbst anrufe, aber trotzdem den Fehler erhalte.$apply
synchron ist (sein Rückruf wird ausgeführt, dann der Code nach $ apply), während dies$timeout
nicht der Fall ist: Wenn der aktuelle Code nach dem Timeout ausgeführt wird, beginnt ein neuer Stapel mit seinem Rückruf, als ob Sie ihn verwenden würdensetTimeout
. Dies kann zu Grafikfehlern führen, wenn Sie zweimal dasselbe Modell aktualisieren:$timeout
Warten Sie, bis die Ansicht aktualisiert wurde, bevor Sie sie erneut aktualisieren.Wenn Sie diesen Fehler erhalten, bedeutet dies im Grunde, dass Ihre Ansicht bereits aktualisiert wird. Sie sollten wirklich nicht
$apply()
in Ihrem Controller anrufen müssen . Wenn Ihre Ansicht nicht wie erwartet aktualisiert wird und Sie diesen Fehler nach dem Aufruf erhalten$apply()
, bedeutet dies höchstwahrscheinlich, dass Sie das Modell nicht korrekt aktualisieren. Wenn Sie einige Details veröffentlichen, können wir das Kernproblem herausfinden.quelle
you're not updating the the model correctly
?$scope.err_message = 'err message';
ist nicht korrektes Update?$apply()
wenn Sie das Modell "außerhalb" von Angular aktualisieren (z. B. über ein jQuery-Plugin). Es ist leicht, in die Falle der Ansicht zu geraten, die nicht richtig aussieht, und so werfen Sie$apply()
überall ein paar s, was dann zu dem Fehler führt, der im OP angezeigt wird. Wenn ich sage,you're not updating the the model correctly
meine ich nur, dass die gesamte Geschäftslogik alles, was in den Geltungsbereich fällt, nicht korrekt ausfüllt, was dazu führt, dass die Ansicht nicht wie erwartet aussieht.Die kürzeste Form des Safes
$apply
ist:quelle
Sie können auch evalAsync verwenden. Es wird irgendwann ausgeführt, nachdem die Verdauung beendet ist!
quelle
Beheben Sie es zunächst nicht auf diese Weise
Dies ist nicht sinnvoll, da $ phase nur ein boolesches Flag für den $ digest-Zyklus ist, sodass Ihr $ apply () manchmal nicht ausgeführt wird. Und denken Sie daran, es ist eine schlechte Praxis.
Verwenden Sie stattdessen
$timeout
Wenn Sie Unterstrich oder lodash verwenden, können Sie defer () verwenden:
quelle
Manchmal werden immer noch Fehler angezeigt, wenn Sie diese Methode verwenden ( https://stackoverflow.com/a/12859093/801426) ).
Versuche dies:
quelle
$rootScope
undanyScope.$root
sind der gleiche Typ.$rootScope.$root
ist redundant.Sie sollten je nach Kontext $ evalAsync oder $ timeout verwenden.
Dies ist ein Link mit einer guten Erklärung:
quelle
versuchen Sie es mit
anstatt
$ applyAsync Planen Sie den Aufruf von $ apply zu einem späteren Zeitpunkt. Dies kann verwendet werden, um mehrere Ausdrücke in die Warteschlange zu stellen, die im selben Digest ausgewertet werden müssen.
HINWEIS: Innerhalb des $ Digest wird $ applyAsync () nur geleert, wenn der aktuelle Bereich $ rootScope ist. Dies bedeutet, dass beim Aufrufen von $ Digest in einem untergeordneten Bereich die $ applyAsync () - Warteschlange nicht implizit geleert wird.
Beispiel:
Verweise:
1. Scope. $ ApplyAsync () vs. Scope. $ EvalAsync () in AngularJS 1.3
quelle
Ich würde Ihnen raten, ein benutzerdefiniertes Ereignis zu verwenden, anstatt einen Digest-Zyklus auszulösen.
Ich habe festgestellt, dass das Senden von benutzerdefinierten Ereignissen und das Registrieren von Listenern für diese Ereignisse eine gute Lösung ist, um eine Aktion auszulösen, die ausgeführt werden soll, unabhängig davon, ob Sie sich in einem Digest-Zyklus befinden oder nicht.
Durch das Erstellen eines benutzerdefinierten Ereignisses werden Sie auch effizienter mit Ihrem Code, da Sie nur Listener auslösen, die dieses Ereignis abonniert haben, und NICHT alle an den Bereich gebundenen Uhren auslösen, wie Sie es tun würden, wenn Sie den Bereich aufrufen. $ Apply.
quelle
yearofmoo hat großartige Arbeit geleistet, um eine wiederverwendbare $ safeApply-Funktion für uns zu erstellen:
Verwendungszweck :
quelle
Ich konnte dieses Problem lösen, indem ich
$eval
statt$apply
an Orten anrief, an denen ich weiß, dass die$digest
Funktion ausgeführt wird.Nach Ansicht der docs ,
$apply
tut im Grunde diese:In meinem Fall
ng-click
ändert a eine Variable innerhalb eines Bereichs, und ein $ watch für diese Variable ändert andere Variablen, die sein müssen$applied
. Dieser letzte Schritt verursacht den Fehler "Digest bereits in Bearbeitung".Durch Ersetzen
$apply
durch$eval
innerhalb des Überwachungsausdrucks werden die Bereichsvariablen wie erwartet aktualisiert.Daher scheint es so , als ob
$eval
Sie nur etwas tun müssen , wenn der Digest aufgrund einer anderen Änderung in Angular trotzdem ausgeführt wird.quelle
Verwenden Sie
$scope.$$phase || $scope.$apply();
stattdessenquelle
Als ich verstand, dass die Angular-Dokumente das Überprüfen
$$phase
eines Anti-Musters aufrufen , versuchte ich zu bekommen$timeout
und_.defer
zu arbeiten.Das Timeout und die verzögerten Methoden erzeugen
{{myVar}}
wie ein FOUT einen Blitz nicht analysierten Inhalts im Dom . Für mich war das nicht akzeptabel. Es lässt mich nicht viel dogmatisch sagen, dass etwas ein Hack ist und keine geeignete Alternative hat.Das einzige, was jedes Mal funktioniert, ist:
if(scope.$$phase !== '$digest'){ scope.$digest() }
.Ich verstehe die Gefahr dieser Methode nicht oder warum sie von den Leuten in den Kommentaren und dem eckigen Team als Hack beschrieben wird. Der Befehl scheint präzise und leicht zu lesen:
In CoffeeScript ist es noch schöner:
scope.$digest() unless scope.$$phase is '$digest'
Was ist das Problem damit? Gibt es eine Alternative, die kein FOUT erzeugt? $ safeApply sieht gut aus, verwendet aber auch die
$$phase
Inspektionsmethode.quelle
Dies ist mein Dienstprogramm:
und dies ist ein Beispiel für seine Verwendung:
quelle
Ich habe diese Methode angewendet und sie scheint einwandfrei zu funktionieren. Dies wartet nur auf die Zeit, die der Zyklus beendet ist, und wird dann ausgelöst
apply()
. Rufen Sie die Funktion einfachapply(<your scope>)
von einem beliebigen Ort aus auf.quelle
Wenn ich den Debugger deaktiviert habe, tritt der Fehler nicht mehr auf. In meinem Fall lag es daran, dass der Debugger die Codeausführung stoppte.
quelle
Ähnlich wie bei den obigen Antworten, aber das hat bei mir treu funktioniert ... in einem Service hinzufügen:
quelle
Sie können verwenden
um den Fehler zu verhindern.
quelle
Das Problem tritt im Grunde genommen auf, wenn wir um Winkel bitten, um den Digest-Zyklus auszuführen, obwohl er gerade ausgeführt wird, was zu Problemen mit dem Winkel zum Verständnis führt. Konsequenz Ausnahme in der Konsole.
1. Es hat keinen Sinn, scope. $ Apply () innerhalb der $ timeout-Funktion aufzurufen, da es intern dasselbe tut.
2. Der Code wird mit der Vanilla-JavaScript-Funktion verwendet, da der native, nicht winkelige Winkel definiert ist, dh setTimeout.
3. Dazu können Sie
if (! Scope. $$ phase) {
scope. $ EvalAsync (function () {
}) verwenden. }}
quelle
Hier ist eine gute Lösung, um diesen Fehler zu vermeiden und $ apply zu vermeiden
Sie können dies mit Debounce (0) kombinieren, wenn Sie aufgrund eines externen Ereignisses aufrufen. Oben ist das von uns verwendete "Entprellen" und ein vollständiges Beispiel für Code
und den Code selbst, um ein Ereignis abzuhören und $ digest nur für den von Ihnen benötigten $ scope aufzurufen
quelle
Gefunden: https://coderwall.com/p/ngisma wo Nathan Walker (am Ende der Seite) einen Dekorateur in $ rootScope vorschlägt, um func 'safeApply' zu erstellen, Code:
quelle
Dies wird Ihr Problem lösen:
quelle