When exactly is ng-checked called

2019-07-13 15:42发布

问题:

While using AngularMaterial, I have ng-checked like this:

<md-list>
    <md-list-item ng-repeat="option in options">
       <p> {{ option }} </p>
       <md-checkbox class="md-secondary" aria-label="{{$index}}" ng-checked="exists($index)" ng-click="toggle($index)"></md-checkbox>
    </md-list-item>
</md-list> 

And my exists function:

$scope.exists = function (optionNum) {
    console.log('Inside $scope.exists. option: '+optionNum);
};

My timer:

function updateTimer() {
    var onTimeout = function(){
      mytimeout = $timeout(onTimeout,1000);
    }
    var mytimeout = $timeout(onTimeout,1000);
}

With this, the $scope.exists function is getting called every second. Can someone please explain how ng-checked and $timeout related ? and how to avoid this ?

回答1:

Reason in one word is: digest cycle. Since your function is bound to the view, every time digest cycle happens those expressions gets evaluated as a part of dirty check to make sure if respective DOM needs to be updated or not. This is nothing to do with angular material alone, it is the core angular implementation. Now in your case you are calling $timeout infinitely which means after each timeout execution digest cycle happens to perform dirty check.

Now what you have is fine, but whenever you bind a function to DOM (as a part of view binding, interpolation or property state attributes or even DOM filters - of course events are fine) you should be aware of the fact that you do not perform extensive operation in that function accidentally or intentionally as the app grows, it will slow down the entire app, and will be hard to refactor and diagnose when the app grows larger and problems starts happening. As much as possible bind to a property instead of a function. Note that even if you bind a property still angular $parse creates a getter on it and adds it to the $$watchers queue to be dirty checked every digest cycle, but difference is that it is a simple getter function.

So basically for instance in your case you could bind ng-checked to property

..ng-checked="doesExist"

and set the property doesExist whenever it needs to be updated. So with this instead of checking for the existence every time, you explicitly set the respective property when a respective event happens. That makes the logic explicit as well.



回答2:

ng-checked, like many of angular's directives are based on watches. Anytime a digest cycle is called, it evaluates all of the watchers (which the function you are using is one of). So every time $timeout evaluates it is starting a new $digest cycle and evaluating all of the watchers. This is part of the "magic" that keeps the view updated with all of the data in your controllers and directives.

Watchers can become a performance issue if you make your functions complex, or make TONS of watchers. It is generally best to have simple logic that returns true or false very quickly and to avoid setting watches on everything.