$ 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?

Vladimir Sidorenko
quelle
1
mögliches Duplikat von Wie man ein Array in AngularJs tief beobachtet?
Lort

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.

rossipedia
quelle
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++;
});
}
Jason
quelle
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
        }
    });
});
Ferenc Takacs
quelle
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);
});
sol4me
quelle
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.

Ramesh Papaganti
quelle
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);
}
Tarun
quelle
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);
Maximillion Bartango
quelle
-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>

Vijay Patel
quelle
8
Beantworten Sie eine zwei Jahre alte Frage mit einer Antwort, die ein fast genaues Duplikat einer vorhandenen Frage ist? Uncool.
Jared Smith