2013-03-12 23 views
52

Ich versuche herauszufinden, warum meine $watch nicht ausgelöst wird. Dies ist ein Ausschnitt aus dem entsprechenden Controller:

$scope.$watch('tasks', function (newValue, oldValue) { 
    //do some stuff 
    //only enters here once 
    //newValue and oldValue are equal at that point 
}); 

$scope.tasks = tasksService.tasks(); 

$scope.addTask = function (taskCreationString) { 
    tasksService.addTask(taskCreationString);//modifies tasks array 
}; 

Auf meiner Sicht ist tasks eindeutig korrekt aktualisiert, da ich wie so gebunden seine Länge haben:

<span>There are {{tasks.length}} total tasks</span> 

Was bin ich?

+0

Es ist wie dieses Problem scheint auch gilt Objekte zu beobachten. Stattdessen müssen Sie eine bestimmte Eigenschaft überwachen. – VSO

Antwort

131

Versuchen Sie $watch('tasks.length', ...) oder $watch('tasks', function(...) { ... }, true).

Standardmäßig überprüft $watch nicht auf Objektgleichheit, sondern nur als Referenz. So wird $watch('tasks', ...) immer einfach die gleiche Array-Referenz zurückgeben, die sich nicht ändert.

Update: Angular v1.1.4 fügt eine $watchCollection() Methode, um diesen Fall zu behandeln:

Shallow-Uhren, die Eigenschaften eines Objekts und ausgelöst wird, wenn eine der Eigenschaften ändern (dies impliziert Arrays das Array beobachten Gegenstände, für Objektkarten bedeutet dies, die Eigenschaften zu beobachten). Wenn eine Änderung festgestellt wird, wird der Rückruf listener ausgelöst.

+0

danke für die Erklärung, ich habe Stunden damit verbracht, nicht zu wissen – asumaran

+3

Das linderte viel Schmerz und Traurigkeit, danke! –

+0

danke. hat mir heute geholfen – VeteranLK

13

Sehr gute Antwort von @Mark. Zusätzlich zu seiner Antwort gibt es eine wichtige Funktion der $watch Funktion, die Sie beachten sollten.

Mit der $watch Funktionsdeklaration wie folgt:

$watch(watch_expression, listener, objectEquality) 

Die $watchZuhörer Funktion nur aufgerufen, wenn der Wert von dem aktuellen Uhr Ausdruck (in Ihrem Fall ist es 'tasks') und der vorherige Aufruf zu Uhr Ausdruck sind nicht gleich. Angular speichert den Wert des Objekts für den späteren Vergleich. Aus diesem Grund hat das Beobachten komplexer Optionen nachteilige Auswirkungen auf Speicher und Leistung. Grundsätzlich gilt der einfachere Uhr Ausdruck Wert desto besser.

+5

+1. Ich werde (der Vollständigkeit halber) erwähnen, dass die Listener-Funktion auch einmal aufgerufen wird, um den Watcher zu initialisieren. Dies muss geschehen, selbst wenn der "watch_expression" nicht geändert wurde. –

+0

yeah ... das Dokument kopiert. https://docs.angularjs.org/api/ng/type/$rootScope.Scope – hupseb

5

würde ich empfehlen,

$scope.$watch('tasks | json', ...) 

versuchen, dass alle Änderungen an den tasks Array fangen, da sie die serialisierten Array als String vergleichen.

+3

Als Vorsichtsmaßnahme würde diese Lösung die Serialisierung zu JSON bei jedem Digest-Zyklus beinhalten, was nicht besonders leistungsfähig ist (insbesondere wenn das Objekt komplex ist) ein oder ein langes Array). Es wird funktionieren, aber es ist ein bisschen wie ein Vorschlaghammer, um eine Nuss zu knacken. – decates

+0

kleiner Leistungstest für verschiedene Objektgrößen wäre interessant. – abimelex

+0

Das brachte mich auf den richtigen Weg, um das gleiche Problem mit Objekten zu beheben. – VSO

2

Für eindimensionale Arrays können Sie $watchCollection verwenden

$scope.names = ['igor', 'matias', 'misko', 'james']; 
$scope.dataCount = 4; 

$scope.$watchCollection('names', function(newNames, oldNames) { 
    $scope.dataCount = newNames.length; 
}); 
Verwandte Themen