admin管理员组

文章数量:1330612

fiddle

I have a list, and I'd like to be notified whenever it changes. A simple $watch expression isn't working, which I'm guessing is because angular is checking for referential equality, not structural equality.

<html ng-app>
    <head>
    </head>
    <body ng-controller="Root">
        <h1>Base Angular Fiddle</h1>
        Times changed: {{timesChanged}}
        <ul>
            <li ng-repeat="name in names">{{name}}</li>
            <li><button ng-click="names.push('another name')">Add</button></li>
        </ul>
    </body>
</html>
​

​function Root($scope) {
    $scope.timesChanged = 0;
    $scope.names = ['foo', 'bar', 'baz'];
    $scope.$watch('names', function() {
        $scope.timesChanged++;
    });
}​

What I'm hoping will happen is that the callback for 'names' is called each time that I call names.push(). Is there a workaround that's remended for this? Or am I just not using $watch correctly?

fiddle

I have a list, and I'd like to be notified whenever it changes. A simple $watch expression isn't working, which I'm guessing is because angular is checking for referential equality, not structural equality.

<html ng-app>
    <head>
    </head>
    <body ng-controller="Root">
        <h1>Base Angular Fiddle</h1>
        Times changed: {{timesChanged}}
        <ul>
            <li ng-repeat="name in names">{{name}}</li>
            <li><button ng-click="names.push('another name')">Add</button></li>
        </ul>
    </body>
</html>
​

​function Root($scope) {
    $scope.timesChanged = 0;
    $scope.names = ['foo', 'bar', 'baz'];
    $scope.$watch('names', function() {
        $scope.timesChanged++;
    });
}​

What I'm hoping will happen is that the callback for 'names' is called each time that I call names.push(). Is there a workaround that's remended for this? Or am I just not using $watch correctly?

Share edited Dec 11, 2012 at 23:14 Nick Heiner asked Dec 11, 2012 at 22:39 Nick HeinerNick Heiner 123k192 gold badges486 silver badges704 bronze badges 3
  • You may have posted the wrong fiddle version. There is no code in the fiddle you posted. – Kyeotic Commented Dec 11, 2012 at 22:50
  • Your fiddle doesn't have this code - can you update? – Roy Truelove Commented Dec 11, 2012 at 22:51
  • Yep, I definitely posted the wrong version - sorry about that. – Nick Heiner Commented Dec 11, 2012 at 23:12
Add a ment  | 

2 Answers 2

Reset to default 9

You are right, angular checks for reference by default, for perfomance reasons. The usage of reference vs equality depends on the value of the $watch function's third argument, as you can see in the docs. Just set it to true (it is false by default) and your fiddle will work - here it is, working.

the change:

$scope.$watch('names', function() {
        $scope.timesChanged++;
    }, true);

EDIT - Performance implications:

Comparing two arrays/objects can obviously be very inefficient (hence the check by reference angular makes by default), depending on the size and plexity of your variables. There is an alternate method - watching the length of the array. This has obvious limitations - changing one element no longer triggers the $watch function - but can be immensely faster. Here is an updated fiddle!

Just a quick addition:

If you want to watch for more special things you can do it like so:

$scope.$watch(function() {
    // Define what you are watching for
    // For example convert your array into a string
    return String($scope.names);
}, function(newValue, oldValue) {
    // Do something cool.
});

This is definitely not as fast as names.length but you could play around and find a sophisticating solution.

If anyone is interested I played around a bit with fast hashing that uses a modified Duff's device: http://jsfiddle/flek/mfKT6/1/ If you are dealing with massive data this hashing method could also be updated to be error responsive (like hash only every 2nd or 3rd character)

本文标签: javascriptHow to watch for changes to a listStack Overflow