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=".6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="TestController">
<p>{{getText()}}</p>
<p>{{'' | text}}</p>
</div>
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./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
Reset to default 5The 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.
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./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>
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.
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 ;)
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 pares 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}}