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);
    });
});

JSFiddle

0
Stafford Williams 7 Aug. 2015 im 18:25

3 Antworten

Beste Antwort

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);
0
Community 23 Mai 2017 im 11:57

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/

4
Daniel A. White 7 Aug. 2015 im 15:26

Sie können {{myValue}} anstelle eines <span> Elements ausprobieren

0
cbender 7 Aug. 2015 im 15:31