0

I was attempting to get AngularJs to apply an active class on a nav section depending upon the active route. My solution works fine but I noticed a curious behaviour -- more on that below. For now, here's how the active class is set in the HTML:

<ul class="menu" ng-controller="ControllerHeader">
  <li><a ng-class="{ active: isActive('/foo') }" href="#/foo">foo</a></li>
  <li><a ng-class="{ active: isActive('/bar') }" href="#/bar">bar</a></li>
  <li><a ng-class="{ active: isActive('/baz') }" href="#/baz">baz</a></li>
</ul>

ControllerHeader and isActive are defined as given:

var app = angular.module('app', [ ]);

app.controller( 'ControllerHeader', ['$scope', '$location',
  function ($scope, $location) {
    $scope.isActive = function (location) {
      // multiple logging occurs: exactly 3*3 times
      console.log(location, $location.path());
      return location === $location.path();
    };
  }
] );

The issue I've noticed is that isActive above is called 3 * 3 times! Could someone please explain why? How can I fix it that such that it is called once per item only?

3
  • It's called everytime a $digest cycle is triggered, which is a lot. Unless it's causing performance issues, don't worry about it. Commented Feb 19, 2015 at 19:24
  • does your route defines the controller as well? hows your route defined? are you using ui-router? or ng-route? Commented Feb 19, 2015 at 19:25
  • First question I had on Angular - a possible duplicate of yours: stackoverflow.com/questions/25908130/… Commented Feb 19, 2015 at 19:26

2 Answers 2

4

If you are using Angular 1.3+, you can use the :: operator to only evaluate the expression once:

<ul class="menu" ng-controller="ControllerHeader">
  <li><a ng-class="::{ active: isActive('/foo') }" href="#/foo">foo</a></li>
  <li><a ng-class="::{ active: isActive('/bar') }" href="#/bar">bar</a></li>
  <li><a ng-class="::{ active: isActive('/baz') }" href="#/baz">baz</a></li>
</ul>

If you are using an older version of Angular, you can use a third party library such as bindonce

As for why this is happening without binding once, indeed it is covered by many other questions. If you want the class to be dynamically set depending on angular context, Angular need to execute the function to check its value. That is called executing the watchers during a digest cycle, and this is done often.

Sign up to request clarification or add additional context in comments.

2 Comments

it doesn't seems to work if binded on a function that return a random value. Is it correct? How can I achieve one time binding with a random function?
No, being random shouldn't matter, the first time the function return something that is not undefined should freeze the value (it doesn't make a difference for the browser if it's random or not). Feel free to create a question and present a reproducible code snippet if you need help
0

All expression that you use in AngularJS get evaluated multiple times when a digest cycle runs. This is done for dirty checking which validates whether the current value of expression is different from the last value.

1 Comment

It could be evaluated multiple times on a digest. Typically, it's once. And this question has been asked in one flavor or another many times. Why not find a duplicate rather than provide yet-another-answer?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.