Manchmal muss ich $scope.$apply
meinen Code verwenden und manchmal wird der Fehler "Digest bereits in Bearbeitung" ausgegeben. Also fing ich an, einen Weg zu finden und fand diese Frage: AngularJS: Verhindern Sie, dass der Fehler $ Digest bereits ausgeführt wird, wenn Sie $ scope aufrufen. $ Apply () . In den Kommentaren (und im eckigen Wiki) können Sie jedoch lesen:
Tun Sie dies nicht, wenn (! $ Scope. $$ Phase) $ scope. $ Apply () bedeutet, dass Ihr $ scope. $ Apply () im Aufrufstapel nicht hoch genug ist.
Jetzt habe ich zwei Fragen:
- Warum genau ist das ein Anti-Muster?
- Wie kann ich $ scope. $ Apply sicher verwenden?
Eine andere "Lösung", um den Fehler "Digest bereits in Bearbeitung" zu verhindern, scheint die Verwendung von $ timeout zu sein:
$timeout(function() {
//...
});
Ist das der richtige Weg? Ist es sicherer? Hier ist also die eigentliche Frage: Wie kann ich die Möglichkeit eines Fehlers "Digest bereits in Bearbeitung" vollständig ausschließen ?
PS: Ich verwende nur $ scope. $ Apply in nicht eckigen Rückrufen, die nicht synchron sind. (Soweit ich weiß, sind dies Situationen, in denen Sie $ scope verwenden müssen. $ apply, wenn Sie möchten, dass Ihre Änderungen angewendet werden.)
quelle
scope
von innerhalb des Winkels oder von außerhalb des Winkels manipulieren . Demnach wissen Sie immer, ob Sie anrufen müssenscope.$apply
oder nicht. Und wenn Sie denselben Code sowohl für die Winkel- als auch für diescope
Nichtwinkelmanipulation verwenden, machen Sie es falsch. Er sollte immer getrennt sein. Wenn Sie also auf einen Fall stoßenscope.$$phase
, in dem Sie überprüfen müssen , ist Ihr Code dies nicht richtig entworfen, und es gibt immer einen Weg, es "richtig" zu machendigest already in progress
FehlerAntworten:
Nach einigem Graben konnte ich die Frage lösen, ob es immer sicher ist, es zu benutzen
$scope.$apply
. Die kurze Antwort lautet ja.Lange Antwort:
Aufgrund der Ausführung von Javascript durch Ihren Browser ist es nicht möglich, dass zwei Digest-Aufrufe zufällig kollidieren .
Daher kann der Fehler "Digest bereits in Bearbeitung" nur in einer Situation auftreten: Wenn ein $ apply in einem anderen $ apply ausgegeben wird, z.
Diese Situation kann nicht entstehen, wenn wir $ scope.apply in einem reinen Rückruf ohne Winkel verwenden, wie zum Beispiel dem Rückruf von
setTimeout
. Der folgende Code ist also 100% kugelsicher und es ist nicht erforderlich, aif (!$scope.$$phase) $scope.$apply()
Auch dieser ist sicher:
Was ist NICHT sicher (weil $ timeout - wie alle Angularjs-Helfer - bereits
$scope.$apply
nach Ihnen verlangt ):Dies erklärt auch, warum die Verwendung von
if (!$scope.$$phase) $scope.$apply()
ein Anti-Muster ist. Sie brauchen es einfach nicht, wenn Sie es$scope.$apply
richtig verwenden: In einem reinen js-Rückruf wiesetTimeout
zum Beispiel.Weitere Informationen finden Sie unter http://jimhoskins.com/2012/12/17/angularjs-and-apply.html .
quelle
$document.bind('keydown', function(e) { $rootScope.$apply(function() { // a passed through function from the controller gets executed here }); });
ich wirklich nicht weiß, warum ich $ hier anwenden muss, weil ich $ document.bind verwende.function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; }
Es gibt dort keine Anwendung.$timeout
Semantisch bedeutet, Code nach einer Verzögerung auszuführen. Es mag eine funktionssichere Sache sein, aber es ist ein Hack. Es sollte eine sichere Möglichkeit geben, $ apply zu verwenden, wenn Sie nicht wissen können, ob ein$digest
Zyklus ausgeführt wird oder Sie sich bereits in einem befinden$apply
.Es ist jetzt definitiv ein Anti-Muster. Ich habe gesehen, wie eine Verdauung explodierte, selbst wenn Sie nach der $$ -Phase suchen. Sie sollten einfach nicht auf die interne API zugreifen, die durch
$$
Präfixe gekennzeichnet ist.Du solltest benutzen
da dies die bevorzugte Methode in Angular ^ 1.4 ist und speziell als API für die Anwendungsschicht verfügbar gemacht wird.
quelle
In jedem Fall, wenn Ihr Digest in Bearbeitung ist und Sie einen anderen Dienst zum Digest drängen, wird einfach ein Fehler ausgegeben, dh der Digest wird bereits ausgeführt. Um dies zu heilen, haben Sie zwei Möglichkeiten. Sie können nach anderen laufenden Digests wie Polling suchen.
Erster
Wenn die obige Bedingung erfüllt ist, können Sie Ihren $ scope anwenden. $ andernfalls nicht anwenden und
Die zweite Lösung ist die Verwendung von $ timeout
Der andere Digest wird erst gestartet, wenn $ timeout seine Ausführung abgeschlossen hat.
quelle
$scope.$apply();
.$timeout
ist der Schlüssel! es funktioniert und später fand ich, dass es auch empfohlen wird.scope.$apply
löst a aus$digest
Zyklus aus, der für die bidirektionale Datenbindung von grundlegender Bedeutung istEin
$digest
Zyklus prüft , ob Objekte (dh Modelle) (um genau zu sein$watch
) angehängt sind,$scope
um festzustellen, ob sich ihre Werte geändert haben. Wenn eine Änderung festgestellt wird, werden die erforderlichen Schritte ausgeführt, um die Ansicht zu aktualisieren.Wenn Sie jetzt verwenden
$scope.$apply
, wird der Fehler "Bereits in Bearbeitung" angezeigt. Es ist also ziemlich offensichtlich, dass ein $ Digest ausgeführt wird, aber was hat ihn ausgelöst?ans -> alle
$http
Anrufe, alle ng-click, wiederholen, zeigen, verstecken usw. lösen einen$digest
Zyklus aus UND DER SCHLECHTESTE TEIL LÄUFT JEDEN GELTUNGSBEREICH.Angenommen, Ihre Seite verfügt über 4 Controller oder Anweisungen A, B, C, D.
Wenn Sie jeweils 4
$scope
Eigenschaften haben, befinden sich auf Ihrer Seite insgesamt 16 $ scope-Eigenschaften.Wenn Sie
$scope.$apply
in Controller D auslösen,$digest
prüft ein Zyklus alle 16 Werte !!! plus alle $ rootScope-Eigenschaften.Antwort -> aber
$scope.$digest
Auslöser ein$digest
auf Kind und gleichen Umfang so wird es nur 4 Objekte überprüfen. Wenn Sie also sicher sind, dass Änderungen in D A, B, C nicht beeinflussen, verwenden Sie$scope.$diges
t not$scope.$apply
.Ein bloßes Klicken oder Ausblenden / Ausblenden kann also einen
$digest
Zyklus für mehr als 100 Eigenschaften auslösen, selbst wenn der Benutzer kein Ereignis ausgelöst hat !quelle
Verwenden
$timeout
Sie es auf die empfohlene Weise.Mein Szenario ist, dass ich Elemente auf der Seite basierend auf den Daten ändern muss, die ich von einem WebSocket erhalten habe. Und da es sich außerhalb von Angular befindet, ohne das $ timeout, wird das einzige Modell geändert, nicht jedoch die Ansicht. Weil Angular nicht weiß, dass diese Daten geändert wurden.
$timeout
sagt Angular im Grunde, er solle die Änderung in der nächsten Runde von $ Digest vornehmen.Ich habe auch folgendes versucht und es funktioniert. Der Unterschied für mich ist, dass $ timeout klarer ist.
quelle
$http
). Andernfalls müssen Sie diesen Code überall wiederholen.$scope.$apply
wenn SiesetTimeout
oder$timeout
Ich fand eine sehr coole Lösung:
injizieren Sie das, wo Sie brauchen:
quelle