admin管理员组

文章数量:1129436

I want to watch for changes in a dictionary, but for some reason watch callback is not called.

Here is a controller that I use:

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

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

Here is fiddle.

I expect $watch callback to be fired each time name or surname is changed, but it doesn't happen.

What is the correct way to do it?

I want to watch for changes in a dictionary, but for some reason watch callback is not called.

Here is a controller that I use:

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

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

Here is fiddle.

I expect $watch callback to be fired each time name or surname is changed, but it doesn't happen.

What is the correct way to do it?

Share Improve this question edited Oct 24, 2018 at 5:36 Prashant Pokhriyal 3,8274 gold badges31 silver badges41 bronze badges asked Oct 18, 2013 at 17:20 Vladimir SidorenkoVladimir Sidorenko 4,2653 gold badges20 silver badges14 bronze badges 1
  • 1 possible duplicate of How to deep watch an array in angularjs? – lort Commented Oct 18, 2013 at 20:58
Add a comment  | 

8 Answers 8

Reset to default 585

Call $watch with true as the third argument:

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

By default when comparing two complex objects in JavaScript, they will be checked for "reference" equality, which asks if the two objects refer to the same thing, rather than "value" equality, which checks if the values of all the properties of those objects are equal.

Per the Angular documentation, the third parameter is for objectEquality:

When objectEquality == true, inequality of the watchExpression is determined according to the angular.equals function. To save the value of the object for later comparison, the angular.copy function is used. This therefore means that watching complex objects will have adverse memory and performance implications.

The form object isn't changing, only the name property is

updated fiddle

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

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

Little performance tip if somebody has a datastore kind of service with key -> value pairs:

If you have a service called dataStore, you can update a timestamp whenever your big data object changes. This way instead of deep watching the whole object, you are only watching a timestamp for change.

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

});

And in a directive you are only watching the timestamp to change

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

The reason why your code doesn't work is because $watch by default does reference check. So in a nutshell it make sure that the object which is passed to it is new object. But in your case you are just modifying some property of form object not creating a new one. In order to make it work you can pass true as the third parameter.

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

It will work but You can use $watchCollection which will be more efficient then $watch because $watchCollection will watch for shallow properties on form object. E.g.

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

As you are looking for form object changes, the best watching approach is to use
$watchCollection. Please have a look into official documentation for different performance characteristics.

Try this:

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

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

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

For anyone that wants to watch for a change to an object within an array of objects, this seemed to work for me (as the other solutions on this page didn't):

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

you must changes 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>

本文标签: javascriptwatch an objectStack Overflow