4

Consider the following example:

    angular.module('app', []).controller('TestController', function($scope) {
      $scope.getText = function() {
          console.log('getting text');
          return 'text';
      };
  }).filter('text', function() {
      return function() {
          console.log('text filter');
          return 'text';
      };
  });
 

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="TestController">
    <p>{{getText()}}</p>
    <p>{{'' | text}}</p>
</div>

Notice that the getText() function runs twice whereas the filter only runs once. I assume the getText() function runs twice to make sure the model is now stable. Why not the same behavior for the filter?

5 Answers 5

5

The documentation is pretty clear on this subject:

In templates, filters are only executed when their inputs have changed. This is more performant than executing a filter on each $digest as is the case with expressions.

Here's the source.

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

Comments

3

Cosmin is exactly right - and here's a demo to prove it (which, coincidentally, will cause a stack overflow at some point) - when getText() is called, it assigns a new value to the input of the text filter, which causes it to re-evaluate, which causes another digest cycle, which causes the filter to reevaluate... which eventually causes something like a stack overflow.


EDIT I removed a testing portion that was causing the overflow - this will only have the filter evaluate twice, since getText is called only twice.

angular.module('app', []).controller('TestController', function($scope) {
  $scope.foo = 'bar';
  $scope.getText = function() {
    console.log('getting text');
    $scope.foo += 'a';

    return 'text';
  };
}).filter('text', function() {
  return function() {
    console.log('text filter');
    return 'text';
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="TestController">
  <p>{{getText()}}</p>
  <p>{{foo | text}}</p>
</div>

Comments

1

When an expression is used in a template, then AngularJS first evaluates the material/text inside braces (Interpolation) and then converts the value/output to string and then insert this string value into the HTML element/attribute.

From AngularJS Docs:- In templates, filters are only executed when their inputs have changed. This is more performant than executing a filter on each $digest as is the case with expressions.

There are two exceptions to this rule:

  • In general, this applies only to filters that take primitive values as inputs. Filters that receive Objects as input are executed on each $digest, as it would be too costly to track if the inputs have changed.
  • Filters that are marked as $stateful are also executed on each $digest. See Stateful filters for more information. Note that no AngularJS core filters are $stateful.

Comments

0

As per documentation,

Filters are only executed when their inputs have changed. This is more performant than executing a filter on each $digest as is the case with expressions.

There are two exceptions to this rule:

In general, this applies only to filters that take primitive values as inputs. Filters that receive Objects as input are executed on each $digest, as it would be too costly to track if the inputs have changed.

Filters that are marked as $stateful are also executed on each $digest. See Stateful filters for more information. Note that no AngularJS core filters are $stateful.

Since you have a primitive value which doesn't change, the filter doesn't have to execute again. Change the empty string '' to an object literal {} and see what happens ;)

Comments

0

Both bindings are being checked twice, but you can only see one being checked twice. In the case of the filter, the input is ''. Angular is only checking the INPUT (source) to the filter when dirty checking and it checks it twice and compares results. So it doesn't call the filter twice.

For the curly brace binding, the RESULT of the expression is checked twice, and so you can see your function being called twice.

But if you make the input to the filter a function like this you'll see it's called twice just like your curly brace binding and the filter is still called only once.

Changing the filter input to a function shows that input is checked twice and filter is called just once:

{{getText() | text}}

Comments

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.