0

In the following code, $watch should execute the function recalculateInfoBox when the variable scores changes. However, it does nothing.

I have read the adding true as the third variable of $watch usually fixes the problem but in this case $watch still does not fire.

What else do I need to change so that $watch fires?

<html ng-app="mainModule">
    <head>
        <style type="text/css">
        </style>
    </head>    
    <body ng-controller="mainController" >

        <div>Scores: <input type="text" ng-model="scores" ng-list style="width:500px" placeholder="Enter comma-separated list..."/></div>
        <div>You have {{scores.length}} items.</div>
        <hr/>
        ng-style:
        <div ng-style="{'width': infoBox.width, 'background-color': infoBox.backgroundColor}">
            This is the info.
        </div>
        <button ng-click="infoBox.width = '300px'">small</button> 
        <button ng-click="infoBox.width = '800px'">large</button> 
        <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
        <script>
                    var mainModule = angular.module('mainModule', []);
                            function mainController($scope) {
                                $scope.scores = [];
                                $scope.infoBox = {width: '300px', backgroundColor: '#ddd'};
                                var recalculateInfoBox = function () {
                                    console.log('in recalc');
                                    $scope.infoBox.width = '100px';
                                };
                                $scope.$watch('scores', 'recalculateInfoBox', true);
                            }
        </script>
    </body>
</html>
Edward Tanguay
  • 189,012
  • 314
  • 712
  • 1,047

2 Answers2

1

$scope.$watch takes a callback function (not the name of one).

So, changing to this should work:

var recalculateInfoBox = function () {
    console.log('in recalc');
    $scope.infoBox.width = '100px';
};
$scope.$watch('scores', recalculateInfoBox, true);

From the docs:

$watch(watchExpression, listener, [objectEquality]);

Registers a listener callback to be executed whenever the watchExpression changes.

Community
  • 1
  • 1
Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
  • Perfect, my oversight, that works now, was confusing it with this kind of syntax, I guess, `myAppModule.controller('TextController',...` – Edward Tanguay Jan 06 '15 at 16:33
  • 1
    It can be a bit confusing :). For example, the first parameter of `$watch` can be a `$scope` property *name* OR a JavaScript *expression* (callback) returning a value that is checked for change. – Davin Tryon Jan 06 '15 at 16:35
1

The issue is not because of object equality but it is more because the listener must be a function reference not the name of the function. Unlike the scope property evaluation, which is the first argument (which is generally provided as a string), angular does not even evaluate the function from the scope (even if you add the method as a property of scope object).

$scope.$watch('scores', 'recalculateInfoBox', true);

should be

$scope.$watch('scores', recalculateInfoBox, true);

angular.module('app', []).controller('ctrl', function($scope) {
  $scope.scores = [];
  $scope.infoBox = {
    width: '300px',
    backgroundColor: '#ddd'
  };
  var recalculateInfoBox = function() {
    console.log(arguments);
    $scope.infoBox.width = '100px';
  };
  $scope.$watch('scores', recalculateInfoBox);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <div>Scores:
    <input type="text" ng-paste="handlePaste($event)" ng-model="scores" ng-list style="width:500px" placeholder="Enter comma-separated list..." />
  </div>
  <div>You have {{scores.length}} items.</div>
  <hr/>ng-style:
  <div ng-style="{'width': infoBox.width, 'background-color': infoBox.backgroundColor}">
    This is the info.
  </div>
  <button ng-click="infoBox.width = '300px'">small</button>
  <button ng-click="infoBox.width = '800px'">large</button>
</div>

And you probably do not even need the object equality when dealing with ng-list, i.e $scope.$watch('scores', recalculateInfoBox); should just work as well.

PSL
  • 123,204
  • 21
  • 253
  • 243