Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controller function getting called twice using ng-show

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!

For the record this and this are not the same problem.

like image 646
MusikAnimal Avatar asked Sep 02 '13 21:09

MusikAnimal


2 Answers

This is by design, not a bug (and it has nothing to do with the AngularJS compiler, as another answer incorrectly stated). ng-show and ng-hide work by "watching" changes of the expression ready().

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 by ready()).

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 by ready() to be a different value (e.g. by mutating an isReady variable which is returned by ready()). 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.

like image 98
Buu Nguyen Avatar answered Sep 19 '22 08:09

Buu Nguyen


There was a bug report on this, and the response was something to do with the compiler doing a double check

This is expected behavior. AngularJS does the call twice to ensure that the model has stabilized before rendering it.

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

like image 3
iConnor Avatar answered Sep 18 '22 08:09

iConnor