3

Recently I am learning Angularjs, my code seems not work as expected: this is my div:

<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="data.name" value=""/>
{{data.count}}
</div>
</div>

my controller is:

<script>
var app = angular.module('myApp',[]);
app.controller('myController', function($scope) {

    $scope.data = {
        name:"tom",
        count = 0
    }

    $scope.$watch('data', function(oldValue,newValue) {
        ++$scope.data.count;
    },true);

})
</script>

what I expect is when I type something in the <input> box, the {{data.count}} will increase by 1 each time. However the code is initially 11 and each time I make changes in the input field, the count is increased by 11, can someone help me find where have I done wrong? Thanks a lot in advance.

Ploppy
  • 14,810
  • 6
  • 41
  • 58
User3301
  • 95
  • 3
  • 8
  • Thanks for all your guys' answers. One of my friends has pointed out my problem. That is, when I watch the full object, there will be many sub-watchers for the elements in the object, when the `name` is changed, the `count` is increased as well, however `count` is also an element in the object which it will invoke watch again and again, this is kinda of a deadlock situation and $digest only works 10 times most, so that's why each time it increases by 11. – User3301 Oct 02 '17 at 11:51

2 Answers2

1

Why this happen?

Watcher calls multiple times because you created watcher for full object data. Flag true will create sub-watcher for every value in object.

Its a proper behavior. I believe you want something like:

$scope.$watch('data', function(oldValue,newValue) {
         if(oldValue.name != newValue.name){
             ++$scope.data.count;
          }

    },true);

Demo Fiddle


The second solution is to watch on name only:

$scope.$watch(function(){
  return $scope.data.name
  }, function(oldValue,newValue) {
      ++$scope.data.count;
});
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Hi Maxim, first thank you for your reply.Your solution is exactly what I want it. But I am still confused with my code. If I create a watcher for the full object `data` then the count should increase by 1 as I only change `name` property in the `data` object each time? – User3301 Oct 02 '17 at 10:25
  • 1
    @User3301 on any object change angular triggers watcher for all vslues. So if u have more complicated object, `true` flag will call more times. So if you will listen on name only - will be best bet – snaggs Oct 02 '17 at 11:00
0

Here is a another way to do it. Use the ng-keydown directive and update the count only when a key is pressed inside the input element.

var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
  $scope.data = {
    name: "tom",
    count: 0
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app='myApp'>
  <input type="text" ng-model="data.name" value="" ng-keydown="data.count = data.count+1" /> {{data.count}}
</div>
Naren Murali
  • 19,250
  • 3
  • 27
  • 54
  • Hi Naren, thanks for your solution. I am an Angular learner so I would be really appreciated it if you can point out where my code's problem is. – User3301 Oct 02 '17 at 10:27
  • @User3301 Firstly there is a `=` in the object `$scope.data` which is making the object invalid in the code of the question, then the reason you are getting 11 can be explained in the answer by `MW`. in this [link](https://stackoverflow.com/questions/16947771/how-do-i-ignore-the-initial-load-when-watching-model-changes-in-angularjs) – Naren Murali Oct 02 '17 at 10:33