$ mirar un objeto

317

Quiero ver los cambios en un diccionario, pero por alguna razón no se llama a la devolución de llamada.

Aquí hay un controlador que uso:

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

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

Aquí está el violín .

Espero que la devolución de llamada $ watch se active cada vez que se cambia el nombre o el apellido, pero no sucede.

¿Cuál es la forma correcta de hacerlo?

Vladimir Sidorenko
fuente

Respuestas:

570

Llame $watchcon truecomo tercer argumento:

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

Por defecto, al comparar dos objetos complejos en JavaScript, se verificará la igualdad de "referencia", que pregunta si los dos objetos se refieren a la misma cosa, en lugar de la igualdad de "valor", que verifica si los valores de todas las propiedades de esos Los objetos son iguales.

Según la documentación angular , el tercer parámetro es para objectEquality:

Cuando objectEquality == true, la desigualdad de watchExpression se determina de acuerdo con la angular.equalsfunción. Para guardar el valor del objeto para una comparación posterior, angular.copyse utiliza la función. Por lo tanto, esto significa que observar objetos complejos tendrá implicaciones adversas de memoria y rendimiento.

rossipedia
fuente
2
Esta es una buena respuesta, pero imagino que hay momentos en los que no querrías mirar un objeto de forma recursiva
Jason
16
Existen. De manera predeterminada, si no pasa true, verificará la igualdad de referencia si el valor observado es un objeto o una matriz. En ese caso, debe especificar explícitamente las propiedades, como $scope.$watch('form.name')y$scope.$watch('form.surname')
rossipedia
3
Gracias. Eso es exactamente lo que me perdí. Jason, tienes razón: no siempre quieres ver objetos recursivos, pero en mi caso era exactamente lo que necesitaba.
Vladimir Sidorenko
1
ImmutableJS y la comparación de referencias pueden ayudar a aumentar el rendimiento.
Dragos Rusu
13

El formobjeto no cambia, solo la namepropiedad es

violín actualizado

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

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

Poco consejo de rendimiento si alguien tiene un tipo de servicio de almacén de datos con clave -> pares de valores:

Si tiene un servicio llamado dataStore , puede actualizar una marca de tiempo cada vez que cambie su objeto de big data. De esta manera, en lugar de observar todo el objeto en profundidad, solo está viendo una marca de tiempo para el cambio.

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

});

Y en una directiva solo estás viendo cómo cambia la marca de tiempo

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
fuente
1
Creo que vale la pena mencionar que en este caso perderías la funcionalidad de tener acceso al valor anterior de los datos que estás almacenando. newVal, oldValen este ejemplo, los valores de la marca de tiempo no son los valores de los objetos observados. No invalida esta respuesta, solo creo que es un inconveniente que vale la pena mencionar.
Michał Schielmann
Es cierto que este método lo uso principalmente cuando quiero cargar una estructura de datos complicada como fuente de algo (una nueva versión de algunos datos, por ejemplo). Si me importan los valores nuevos y antiguos, no los almacenaría en objetos grandes y complicados, sino que tendría una pequeña representación de texto de lo que me interesa y vería si cambia o lo obtengo cuando cambie la marca de tiempo.
Ferenc Takacs
5

La razón por la cual su código no funciona es porque, $watchde manera predeterminada, hace una verificación de referencia. En pocas palabras, asegúrese de que el objeto que se le pasa es un objeto nuevo. Pero en su caso, solo está modificando alguna propiedad del objeto de formulario para no crear una nueva. Para que funcione, puede pasar truecomo tercer parámetro.

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

Funcionará, pero puede usar $ watchCollection, que será más eficiente que $ watch porque $watchCollectionbuscará propiedades poco profundas en el objeto de formulario. P.ej

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

Mientras busca cambios en los objetos de formulario, el mejor enfoque de observación es usar
$watchCollection. Consulte la documentación oficial para conocer las diferentes características de rendimiento.

Ramesh Papaganti
fuente
1

Prueba esto:

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

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

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

Para cualquiera que quiera ver un cambio en un objeto dentro de una matriz de objetos, esto pareció funcionar para mí (ya que las otras soluciones en esta página no lo hicieron):

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
fuente
-3

debes cambiar en $ 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
fuente
8
¿Responder una pregunta de dos años con una respuesta que es casi un duplicado exacto de una existente? No es genial
Jared Smith