$ regarder un objet

317

Je veux surveiller les changements dans un dictionnaire, mais pour une raison quelconque, le rappel de la montre n'est pas appelé.

Voici un contrôleur que j'utilise:

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

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

Voici le violon .

Je m'attends à ce que le rappel $ watch soit déclenché à chaque changement de nom ou de prénom, mais cela ne se produit pas.

Quelle est la bonne façon de procéder?

Vladimir Sidorenko
la source

Réponses:

570

Appelez $watchavec truecomme troisième argument:

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

Par défaut, lors de la comparaison de deux objets complexes en JavaScript, leur égalité de "référence" sera vérifiée, ce qui demandera si les deux objets se réfèrent à la même chose, plutôt que l'égalité de "valeur", qui vérifie si les valeurs de toutes les propriétés de celles-ci les objets sont égaux.

Selon la documentation angulaire , le troisième paramètre est pour objectEquality:

Quand objectEquality == true, l'inégalité de watchExpression est déterminée en fonction de la angular.equalsfonction. Pour enregistrer la valeur de l'objet pour une comparaison ultérieure, la angular.copyfonction est utilisée. Cela signifie donc que regarder des objets complexes aura des répercussions négatives sur la mémoire et les performances.

rossipedia
la source
2
C'est une bonne réponse, mais j'imagine qu'il y a des moments où vous ne voudriez pas regarder récursivement un objet
Jason
16
Il y a. Par défaut, si vous ne réussissez pas true, il vérifiera l'égalité de référence si la valeur surveillée est un objet ou un tableau. Dans ce cas, vous devez spécifier explicitement les propriétés, comme $scope.$watch('form.name')et$scope.$watch('form.surname')
rossipedia
3
Merci. C'est exactement ce que j'ai manqué. Jason, vous avez raison - vous ne voulez pas toujours regarder des objets récursifs, mais dans mon cas, c'était exactement ce dont j'avais besoin.
Vladimir Sidorenko du
1
ImmutableJS et la comparaison de référence peuvent aider à augmenter les performances.
Dragos Rusu
13

L' formobjet ne change pas, seule la namepropriété est

violon mis à jour

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

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

Petit conseil de performance si quelqu'un a un type de service de banque de données avec des paires clé -> valeur:

Si vous disposez d'un service appelé dataStore , vous pouvez mettre à jour un horodatage chaque fois que votre objet Big Data change. De cette façon, au lieu de regarder en profondeur l'objet entier, vous ne regardez qu'un horodatage pour le changement.

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

});

Et dans une directive, vous ne regardez que l'horodatage pour changer

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
la source
1
Je pense qu'il vaut la peine de mentionner que dans ce cas, vous perdriez la fonctionnalité d'avoir accès à l'ancienne valeur des données que vous stockez. newVal, oldValdans cet exemple, les valeurs d'horodatage ne sont pas les valeurs d'objets observées. Cela ne rend pas cette réponse invalide, je pense simplement que c'est un inconvénient qui mérite d'être mentionné.
Michał Schielmann
Certes, cette méthode que j'utilise principalement lorsque je veux charger une structure de données compliquée comme source pour quelque chose (une nouvelle version de certaines données par exemple). Si je me soucie des valeurs nouvelles et anciennes, je ne les stockerais pas dans de gros objets compliqués, j'aurais plutôt une petite représentation textuelle de la chose qui m'intéresse et je regarde cela pour le changer ou l'obtenir lorsque l'horodatage change.
Ferenc Takacs
5

La raison pour laquelle votre code ne fonctionne pas est que, $watchpar défaut, il vérifie les références. Donc, en un mot, il s'assure que l'objet qui lui est transmis est un nouvel objet. Mais dans votre cas, vous modifiez simplement certaines propriétés d'un objet de formulaire et n'en créez pas de nouvelles. Pour le faire fonctionner, vous pouvez passer truecomme troisième paramètre.

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

Cela fonctionnera mais vous pouvez utiliser $ watchCollection qui sera plus efficace que $ watch car $watchCollectionsurveillera les propriétés superficielles sur l'objet de formulaire. Par exemple

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

Lorsque vous recherchez des modifications d'objet de formulaire, la meilleure approche de surveillance consiste à utiliser
$watchCollection. Veuillez consulter la documentation officielle pour les différentes caractéristiques de performance.

Ramesh Papaganti
la source
1

Essaye ça:

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

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

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

Pour tous ceux qui souhaitent surveiller la modification d'un objet dans un tableau d'objets, cela semble fonctionner pour moi (contrairement aux autres solutions de cette page):

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
la source
-3

vous devez changer dans $ 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
la source
8
Répondre à une question vieille de deux ans avec une réponse qui est une copie presque exacte d'une question existante? Pas cool.
Jared Smith