I have a very simple Angular app setup, code as follows:
index.html
<!DOCTYPE html>
<html>
<head>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js'></script>
<script src='app.js'></script>
</head>
<body ng-app="app">
<div ng-controller="MyCtrl">
<div ng-show="ready()">
Some content
</div>
</div>
</body>
</html>
And app.js
var app = angular.module('app', []);
app.controller('MyCtrl', function($scope) {
console.log("MyCtrl called")
$scope.ready = function() {
console.log("ready called");
return true;
}
})
If you run this with your console open you will see MyCtrl called once and ready called twice. I have spent hours trying to figure this out, I see no reason why $scope.ready
would be called anything but exactly one time.
If you use a Angular v1.1.5 and use ng-if
instead of ng-show
you will have the same behaviour, but if you use ng-init
it correctly calls $scope.ready
one time. In my case I will need ng-show
or ng-if
.
Plunkr: http://plnkr.co/edit/ZSwVNLeFSuhbouXZu9SM?p=preview
Clarification:
To elaborate on what I'm aiming for, let's say $scope.ready
at some point later returns false
(maybe it makes an AJAX call, that should NOT be called more than once), I would like "Some content" to no longer be visible. That is, dynamic behaviour based on the result of $scope.ready
.
Any ideas? Thank you for the help!
This is by design, not a bug (and it has nothing to do with the AngularJS compiler, as another answer incorrectly stated).
ng-show
andng-hide
work by "watching" changes of the expressionready()
.At each digest cycle, for every watch, AngularJS evaluates the associated expression to see if there's any change and if there is, invoking the listener (in the case of
ng-show
/ng-hide
, the listener will show or hide the element based on the value returned byready()
).Now, AngularJS can't just be satisfied with the first value returned by
ready()
because during the same digest cycle, something (e.g. another watch expression) might actually make some change that causes whatever returned byready()
to be a different value (e.g. by mutating anisReady
variable which is returned byready()
). Obviously, developers would expect the latest value to be reflected to the DOM.Therefore, AngularJS will evaluate every watch expression at least once to make sure that it is "stabilized" before finishing a digest cycle. Of course, this can lead to infinite digest iterations if an expression keeps changing, so AngularJS will throw an error if a digest cycle could not finish within 10 iterations.
There was a bug report on this, and the response was something to do with the compiler doing a double check
There should be more info on why that is here http://docs.angularjs.org/guide/compiler
Bug Report: https://github.com/angular/angular.js/issues/1146