$ guarda un oggetto


317

Voglio guardare per le modifiche in un dizionario, ma per qualche motivo non viene chiamato watch callback.

Ecco un controller che uso:

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

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

Ecco il violino .

Mi aspetto che $ watch callback venga attivato ogni volta che si cambia nome o cognome, ma non succede.

Qual è il modo corretto di farlo?


Risposte:


570

Chiama $watchcon truecome terzo argomento:

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

Per impostazione predefinita quando si confrontano due oggetti complessi in JavaScript, verranno controllati per l'uguaglianza "di riferimento", che chiede se i due oggetti si riferiscono alla stessa cosa, piuttosto che l'uguaglianza di "valore", che controlla se i valori di tutte le proprietà di quelli gli oggetti sono uguali.

Secondo la documentazione angolare , il terzo parametro è per objectEquality:

Quando objectEquality == true, la disuguaglianza di watchExpression è determinata in base alla angular.equalsfunzione. Per salvare il valore dell'oggetto per un confronto successivo, angular.copyviene utilizzata la funzione. Ciò significa quindi che la visione di oggetti complessi avrà ripercussioni negative sulla memoria e sulle prestazioni.


2
Questa è una buona risposta, ma immagino ci siano momenti in cui non vorrai guardare ricorsivamente un oggetto
Jason,

16
Ci sono. Per impostazione predefinita, se non si passa true, verrà verificata l'uguaglianza di riferimento se il valore osservato è un oggetto o un array. In tal caso, dovresti specificare esplicitamente le proprietà, come $scope.$watch('form.name')e$scope.$watch('form.surname')
rossipedia

3
Grazie. Questo è esattamente quello che mi mancava. Jason, hai ragione: non sempre vuoi guardare oggetti ricorsivi, ma nel mio caso era esattamente quello di cui avevo bisogno.
Vladimir Sidorenko,

1
ImmutableJS e il confronto di riferimento possono aiutare ad aumentare le prestazioni.
Dragos Rusu,

13

L' formoggetto non sta cambiando, solo la nameproprietà lo è

violino aggiornato

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

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

12

Piccolo consiglio prestazionale se qualcuno ha un tipo di servizio datastore con coppie chiave -> valore:

Se disponi di un servizio chiamato dataStore , puoi aggiornare un timestamp ogni volta che cambia l'oggetto big data. In questo modo invece di guardare in profondità l'intero oggetto, stai solo guardando un timestamp per il cambiamento.

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

});

E in una direttiva stai solo guardando il timestamp per cambiare

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

1
Penso che valga la pena ricordare che in questo caso perderai la funzionalità di avere accesso al vecchio valore dei dati che stai memorizzando. newVal, oldValin questo esempio sono i valori di data e ora non i valori degli oggetti osservati. Non rende questa risposta non valida, penso solo che sia uno svantaggio che vale la pena menzionare.
Michał Schielmann,

È vero, questo metodo che uso principalmente quando voglio caricare una struttura di dati complicata come fonte di qualcosa (ad esempio una nuova versione di alcuni dati). Se mi interessano i valori vecchi e nuovi, non li memorizzerei in grandi oggetti complicati, piuttosto avrei una piccola rappresentazione testuale della cosa a cui tengo e guarderei per il cambiamento o lo ottengo quando cambia il timestamp.
Ferenc Takacs,

5

Il motivo per cui il codice non funziona è perché $watchper impostazione predefinita fa riferimento al controllo. Quindi, in poche parole, assicurarsi che l'oggetto che gli viene passato sia un nuovo oggetto. Ma nel tuo caso stai solo modificando alcune proprietà dell'oggetto modulo senza crearne una nuova. Per farlo funzionare puoi passare truecome terzo parametro.

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

Funzionerà ma puoi usare $ watchCollection che sarà più efficiente di $ watch perché $watchCollectioncontrollerà le proprietà superficiali sull'oggetto modulo. Per esempio

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

4

Mentre cerchi cambiamenti agli oggetti del modulo, il miglior approccio di osservazione è usare
$watchCollection. Dai un'occhiata alla documentazione ufficiale per le diverse caratteristiche prestazionali.


1

Prova questo:

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

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

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

0

Per chiunque voglia vedere un cambiamento in un oggetto all'interno di una matrice di oggetti, questo sembra funzionare per me (come non hanno fatto le altre soluzioni in questa pagina):

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

devi cambiare 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
Rispondere a una domanda di due anni con una risposta che è un duplicato quasi esatto di una esistente? Non fico.
Jared Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.