$ ein Objekt beobachten


317

Ich möchte nach Änderungen in einem Wörterbuch suchen, aber aus irgendeinem Grund wird der Rückruf nicht aufgerufen.

Hier ist ein Controller, den ich benutze:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    $scope.$watch('form', function(newVal, oldVal){
        console.log('changed');
    });
}

Hier ist Geige .

Ich erwarte, dass $ watch callback jedes Mal ausgelöst wird, wenn der Vor- oder Nachname geändert wird, aber das passiert nicht.

Was ist der richtige Weg, um es zu tun?


Antworten:


570

Rufen Sie $watchmit trueals drittes Argument:

$scope.$watch('form', function(newVal, oldVal){
    console.log('changed');
}, true);

Wenn zwei komplexe Objekte in JavaScript verglichen werden, werden sie standardmäßig auf "Referenz" -Gleichheit überprüft, wobei gefragt wird, ob sich die beiden Objekte auf dasselbe beziehen, und nicht auf "Wert" -Gleichheit, bei der überprüft wird, ob die Werte aller Eigenschaften dieser Objekte vorliegen Objekte sind gleich.

Gemäß der Angular-Dokumentation gilt der dritte Parameter für objectEquality:

Wann objectEquality == truewird die Ungleichung des watchExpression entsprechend der angular.equalsFunktion bestimmt. Um den Wert des Objekts für einen späteren Vergleich zu speichern, wird die angular.copyFunktion verwendet. Dies bedeutet daher, dass das Betrachten komplexer Objekte nachteilige Auswirkungen auf das Gedächtnis und die Leistung hat.


2
Dies ist eine gute Antwort, aber ich stelle mir vor, es gibt Zeiten, in denen Sie ein Objekt nicht rekursiv betrachten möchten
Jason

16
Es gibt. Wenn Sie nicht übergeben true, wird standardmäßig auf Referenzgleichheit geprüft, ob der überwachte Wert ein Objekt oder ein Array ist. In diesem Fall sollten Sie die Eigenschaften explizit angeben, wie $scope.$watch('form.name')und$scope.$watch('form.surname')
rossipedia

3
Vielen Dank. Genau das habe ich vermisst. Jason, du hast recht - du willst nicht immer rekursive Objekte beobachten, aber in meinem Fall war es genau das, was ich brauchte.
Vladimir Sidorenko

1
Unveränderliches JS und Referenzvergleich können zur Leistungssteigerung beitragen.
Dragos Rusu

13

Das formObjekt ändert sich nicht, nur die nameEigenschaft

Geige aktualisiert

function MyController($scope) {
$scope.form = {
    name: 'my name',
}

$scope.changeCount = 0;
$scope.$watch('form.name', function(newVal, oldVal){
    console.log('changed');
    $scope.changeCount++;
});
}

12

Kleiner Leistungstipp, wenn jemand einen Datenspeicherdienst mit Schlüssel -> Wertepaaren hat:

Wenn Sie einen Dienst namens dataStore haben , können Sie einen Zeitstempel aktualisieren, wenn sich Ihr Big-Data-Objekt ändert. Auf diese Weise beobachten Sie nicht das gesamte Objekt genau, sondern nur einen Zeitstempel für Änderungen.

app.factory('dataStore', function () {

    var store = { data: [], change: [] };

    // when storing the data, updating the timestamp
    store.setData = function(key, data){
        store.data[key] = data;
        store.setChange(key);
    }

    // get the change to watch
    store.getChange = function(key){
        return store.change[key];
    }

    // set the change
    store.setChange = function(key){
        store.change[key] = new Date().getTime();
    }

});

Und in einer Direktive beobachten Sie nur, wie sich der Zeitstempel ändert

app.directive("myDir", function ($scope, dataStore) {
    $scope.dataStore = dataStore;
    $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
        if(newVal !== oldVal && newVal){
            // Data changed
        }
    });
});

1
Ich denke, es ist erwähnenswert, dass Sie in diesem Fall die Funktionalität verlieren würden, auf den alten Wert der von Ihnen gespeicherten Daten zuzugreifen. newVal, oldValIn diesem Beispiel sind die Zeitstempel - Werte nicht die beobachteten Objekte Werte. Diese Antwort wird dadurch nicht ungültig. Ich denke nur, dass dies ein erwähnenswerter Nachteil ist.
Michał Schielmann

Diese Methode verwende ich meistens, wenn ich eine komplizierte Datenstruktur als Quelle für etwas laden möchte (zum Beispiel eine neue Version einiger Daten). Wenn ich mich für neue und alte Werte interessiere, würde ich sie nicht in großen, komplizierten Objekten speichern, sondern ich würde eine kleine Textdarstellung der Sache haben, die mir wichtig ist, und diese auf Änderungen beobachten oder sie erhalten, wenn sich der Zeitstempel ändert.
Ferenc Takacs

5

Der Grund, warum Ihr Code nicht funktioniert, liegt darin, dass $watchstandardmäßig die Referenzprüfung durchgeführt wird. Kurz gesagt, stellen Sie sicher, dass das Objekt, das an das Objekt übergeben wird, ein neues Objekt ist. In Ihrem Fall ändern Sie jedoch nur eine Eigenschaft des Formularobjekts und erstellen keine neue. Damit es funktioniert, können Sie trueals dritten Parameter übergeben.

$scope.$watch('form', function(newVal, oldVal){
    console.log('invoked');
}, true);

Es wird funktionieren, aber Sie können $ watchCollection verwenden, was effizienter ist als $ watch, da $watchCollectionauf flache Eigenschaften des Formularobjekts geachtet wird . Z.B

$scope.$watchCollection('form', function (newVal, oldVal) {
    console.log(newVal, oldVal);
});

4

Wenn Sie nach Änderungen an Formularobjekten suchen, sollten Sie diese am besten beobachten
$watchCollection. Bitte schauen Sie in die offizielle Dokumentation für verschiedene Leistungsmerkmale.


1

Versuche dies:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    function track(newValue, oldValue, scope) {
        console.log('changed');
    };

    $scope.$watch('form.name', track);
}

0

Für alle, die nach einer Änderung an einem Objekt innerhalb einer Reihe von Objekten suchen möchten, schien dies für mich zu funktionieren (wie die anderen Lösungen auf dieser Seite nicht):

function MyController($scope) {

    $scope.array = [
        data1: {
            name: 'name',
            surname: 'surname'
        },
        data2: {
            name: 'name',
            surname: 'surname'
        },
    ]


    $scope.$watch(function() {
        return $scope.data,
    function(newVal, oldVal){
        console.log(newVal, oldVal);
    }, true);

-3

Sie müssen Änderungen in $ watch ....

function MyController($scope) {
    $scope.form = {
        name: 'my name',
    }

    $scope.$watch('form.name', function(newVal, oldVal){
        console.log('changed');
     
    });
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app>
    <div ng-controller="MyController">
        <label>Name:</label> <input type="text" ng-model="form.name"/>
            
        <pre>
            {{ form }}
        </pre>
    </div>
</div>


8
Beantworten Sie eine zwei Jahre alte Frage mit einer Antwort, die ein fast genaues Duplikat einer vorhandenen Frage ist? Uncool.
Jared Smith
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.