Ich versuche zu verstehen, wie man Eigenschaften über einen Controller richtig manipuliert. Der folgende Code führt sechs Aktualisierungen innerhalb von vier Sekunden aus. Die Updates zwei und drei werden in der Ansicht nicht angezeigt. Warum ist das so und was muss ich tun, damit Aktualisierungen dieser Art die Ansicht beeinflussen?
HTML
<div ng-controller="Controller">
myValue: <span ng-bind="myValue"></span>
</div>
Javascript
var app = angular.module('myApp', []);
app.controller('Controller', function ($scope, $interval) {
$scope.myValue = "first";
console.log($scope.myValue);
setTimeout(function() {
$scope.myValue = "second"; // never updates
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // never updates
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);
$interval(function() {
$scope.myValue = "fifth";
console.log($scope.myValue);
$interval(function() {
$scope.$emit("my-event", "sixth");
}, 1000, 1);
}, 3000, 1);
$scope.$on('my-event', function (event, arg) {
$scope.myValue = arg;
console.log(arg);
});
});
3 Antworten
Daher war ich in der ursprünglichen Frage offensichtlich nicht klar genug, da die (positiv) vorgeschlagene Antwort nahe legt, $timeout
anstelle von setTimeout
zu verwenden. Die ursprüngliche Absicht war jedoch, warum zu verstehen Die Aktualisierungen wurden nicht in der Ansicht wiedergegeben, und was getan werden kann, damit diese Arten von Aktualisierungen (, die außerhalb des Winkels entstehen) die Ansicht wie beabsichtigt beeinflussen.
Lesen Sie die Scope-Anleitung
Während ich mich entschied, den Abschnitt Bereiche des Entwicklerhandbuchs zu überspringen, weil er am meisten zu sein schien langweilig, es war wahrscheinlich das wichtigste, und es zeigt deutlich einige Punkte auf, die für das Verständnis der Winkelbindung von Daten unerlässlich sind, insbesondere den Scope Life Cycle , der notiert;
Wenn der Browser JavaScript aufruft, wird der Code außerhalb von ausgeführt Winkelausführungskontext, was bedeutet, dass Angular nichts davon weiß Modelländerungen. Um Modelländerungen richtig zu verarbeiten, müssen Sie die Die Ausführung muss mit
$apply
in den Angular-Ausführungskontext eingegeben werden. Methode. Nur Modelländerungen, die innerhalb von$apply
ausgeführt werden Die Methode wird von Angular ordnungsgemäß berücksichtigt.
Es gibt eine ausgezeichnete Antwort hier, die dieses Konzept weiter erklärt. Die erste Aussage bekräftigt treffend, wie wichtig es ist, den Umfang zu verstehen:
Sie müssen sich darüber im Klaren sein, wie Angular funktioniert, um es zu verstehen.
Rufen Sie nicht einfach $ scope auf. $ Apply
Sie fangen also an, $scope.$apply
um den Ort herum Anrufe hinzuzufügen, um diese Dinge zu berücksichtigen, die außerhalb des Winkels entstehen, aber dann bekommen Sie schließlich:
Error: $digest already in progress
Das heißt, Sie können $scope.$apply
nicht aufrufen, während $digest
ausgeführt wird. Danach können Sie sich überlegen, wie ich $scope.$apply
bedingt aufrufen kann, je nachdem, ob $digest
gerade ausgeführt wird. Aber das musst du nicht tun ...
Verwenden Sie einfach $ timeout
Hah, wie die positiv bewertete Antwort, weiß ich, aber basierend auf einem anderen Denkprozess, denke ich. Siehe diese Antwort. $timeout
wird nicht nur anstelle von setTimeout
verwendet, sondern auch (ohne delay
), um alle Modellaktualisierungen zu verpacken, die von außerhalb des Scope-Lebenszyklus aufgerufen werden So wird sichergestellt, dass kein Konflikt mit einer aktuell verarbeiteten $digest
besteht.
Einpacken
Im ursprünglichen Codeausschnitt werden die zweite und dritte Aktualisierung nicht in der Ansicht wiedergegeben, da sie außerhalb des Angular-Ausführungskontexts ausgeführt werden. Die Tatsache, dass das dritte Update das Modell nicht beeinflusst, bedeutet auch, dass Sie durch das Aufrufen von Ereignissen außerhalb des Ausführungskontexts auch nicht in den Ausführungskontext gelangen.
Das vierte Update ist bereits in $interval
eingeschlossen, wodurch die Aktualisierungen dieses Codes beim nächsten Digest ausgeführt werden. Daher wird der Code wie folgt aktualisiert, um ein Beispiel für ein Ereignis außerhalb des Winkelausführungskontexts anzuzeigen, bei dem die Aktualisierungen in der Ansicht angezeigt werden:
setTimeout(function() {
$timeout(function({ // start wrap
$scope.myValue = "second"; // now this updates!
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // now this updates!
})); // end wrap
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);
Verwenden Sie $timeout
anstelle von setTimeout
, um sich für den Digest-Zyklus anzumelden. second
wird nicht angezeigt, da der Digest-Zyklus den Wert von myValue
überschreibt.
Aktualisierte Geige: https://jsfiddle.net/d9gbpddy/4/
Sie können {{myValue}}
anstelle eines <span>
Elements ausprobieren