Why doesn't Angular update the ng-show status when using an expression?

I have no problem getting ng-show to work when I use a scoped variable:

(excerpt from html file :)

<li ng-show="hasBattery">
  <span>Battery</span>
  <dl>
    <dt>Type</dt>
    <dd>{{phone.battery.type}}</dd>
  </dl>
</li>

      

using the following controller code which works great:

phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', '$http',
  function($scope, $routeParams, $http) {

    function loadData() {
      // fyi, the ?date nonsense below is to avoid browser cacheing of the json file 
      $http.get('phones/' + $routeParams.phoneId + '.json' + '?' + 
                new Date().getTime()).success(function(data) {
        $scope.phone = data; 
        $scope.hasBattery = !$scope.isEmpty($scope.phone.battery.type);
        // should i call some form of $scope.$apply() here? when i do,
        // i get a digest already in progress error. ng-show seems to 
        // update fine without a call to $apply
      });
    };

    $scope.isEmpty = function(str) {
      var retVal = (typeof(str) === undefined || !str || 0 === str.length);
      alert("isempty: returning " + retVal);
      return retVal;
    };

    $scope.hasBattery = false;
    loadData();
  }
]);

      

As I said, it works. When I view a page where phone.battery.type is undefined or empty, ng-show is hiding the div correctly. When I view the page where phone.battery.type is a non-empty string, ng-show shows the div correctly. Things are good.

Here's my question: when I use the call! isEmpty (...) directly in the ng-show statement, instead of using $ scope.hasBattery as an intermediary, it doesn't work.

Here's my code for this version:

<li ng-show="{{!isEmpty(phone.battery.type)}}">
  <span>Battery</span>
  <dl>
    <dt>Type</dt>
    <dd>{{phone.battery.type}}</dd>
  </dl>
</li>

      

with this hasBattery removed:

phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', '$http',
  function($scope, $routeParams, $http) {

    function loadData() {
      $http.get('phones/' + $routeParams.phoneId + '.json' + '?' + new Date().getTime()).success(function(data) {
        $scope.phone = data; 
        // no longer do any evaluation of isEmpty here.
        // as before, I'm not calling $scope.$apply() at all
      });
    };

    // same code as before
    $scope.isEmpty = function(str) {
      var retVal = (typeof(str) === undefined || !str || 0 === str.length);
      alert("isempty: returning " + retVal);
      return retVal;
    };

    loadData();
  }
]);

      

When I load a page where phone.battery.type is a non-empty string that I want to show ng-show, it cannot show the div. I can see that isEmpty is indeed called after the data is loaded and correctly returns false (so the ng-show expression, isEmpty (...), is true). But it looks like Angular doesn't do anything with the value of this expression!

alert dialog box shows that isEmpty is being called and correctly returning false ...

Any idea what's going on? I would think it is a $ scope problem. $ Apply as I've seen elsewhere on SO, but the expression seems to be being evaluated using modern data.

+3


source to share


1 answer


It looks like the {{}} notation means that Angular only evaluates and uses the expression inside the curly braces once.

It looks like either Angular has changed its handling of curly braces in the last year or so, or this blog post I used is incorrect: http://www.whitneyland.com/2013/02/why-does-angularjs-sometimes -use-single-braces-sometimes-double-braces-and-sometimes-no-braces.html

Commentators Kirill D.D., Razakh and Mohammad Sepahwand, thanks for pointing out that removing {{}} solves my problem.



I'm still very surprised that Angular seems to be evaluating the expression more than once - many times, in fact, as it reads by 12 or so time, I have to dismiss the alert () being called from isEmpty!

Why is Angular evaluating an expression that it knows it won't do anything with?

0


source







All Articles