Filters
Angular Filters are typically used to format expressions in bindings in your template. They transform the input data to a new formatted data type.
Formatting a String With a Currency Filter
Problem
You wish to format the amount of currency with a localized currency label.
Solution
Use the built-in currency filter and make sure you load the corresponding locale file for the user’s language.
1 <html>
2 <head>
3 <meta charset='utf-8'>
4 <script src="js/angular.js"></script>
5 <script src="js/angular-locale_de.js"></script>
6 </head>
7 <body ng-app>
8 <input type="text" ng-model="amount" placeholder="Enter amount"/>
9 <p>Default Currency: {{ amount | currency }}</p>
10 <p>Custom Currency: {{ amount | currency: "Euro" }}</p>
11 </body>
12 </html>
Enter an amount and it will be displayed using Angular’s default locale.
You can find the complete example on github.
Discussion
In our example we explicitly load the German locale settings and therefore the default formatting will be in German. The English locale is shipped by default, so there’s no need to include the angular-locale_en.js file. If you remove the script tag, you will see the formatting change to English instead. This means in order for a localized application to work correctly you need to load the corresponding locale file. All available locale files can be seen on github.
Implementing a Custom Filter to Reverse an Input String
Problem
You wish to reverse user’s text input.
Solution
Implement a custom filter, which reverses the input.
1 <body ng-app="MyApp">
2 <input type="text" ng-model="text" placeholder="Enter text"/>
3 <p>Input: {{ text }}</p>
4 <p>Filtered input: {{ text | reverse }}</p>
5 </body>
6
7 var app = angular.module("MyApp", []);
8
9 app.filter("reverse", function() {
10 return function(input) {
11 var result = "";
12 input = input || "";
13 for (var i=0; i<input.length; i++) {
14 result = input.charAt(i) + result;
15 }
16 return result;
17 };
18 });
You can find the complete example on github.
Discussion
Angular’s filter function expects a filter name and a function as params. The function must return the actual filter function where you implement the business logic. In this example it will only have an input param. The result will be returned after the for loop has reversed the input.
Passing Configuration Params to Filters
Problem
You wish to make your filter customizable by introducing config params.
Solution
Angular filters can be passed a hash of params which can be directly accessed in the filter function.
1 <body ng-app="MyApp">
2 <input type="text" ng-model="text" placeholder="Enter text"/>
3 <p>Input: {{ text }}</p>
4 <p>Filtered input: {{ text | reverse: { suffix: "!"} }}</p>
5 </body>
6
7 var app = angular.module("MyApp", []);
8
9 app.filter("reverse", function() {
10 return function(input, options) {
11 input = input || "";
12 var result = "";
13 var suffix = options["suffix"] || "";
14
15 for (var i=0; i<input.length; i++) {
16 result = input.charAt(i) + result;
17 }
18
19 if (input.length > 0) result += suffix;
20
21 return result;
22 };
23 });
You can find the complete example on github.
Discussion
The suffix ! is passed as an option to the filter function and is appended to the output. Note that we check if an actual input exists since we don’t want to render the suffix without any input.
Filtering a List of DOM Nodes
Problem
You wish to filter a ul list of names.
Solution
As well as with strings as input, Angular’s filters also work with arrays.
1 <body ng-app="MyApp">
2 <ul ng-init="names = ['Peter', 'Anton', 'John']">
3 <li ng-repeat="name in names | exclude:'Peter' ">
4 <span>{{name}}</span>
5 </li>
6 </ul>
7 </body>
8
9 var app = angular.module("MyApp", []);
10
11 app.filter("exclude", function() {
12 return function(input, exclude) {
13 var result = [];
14 for (var i=0; i<input.length; i++) {
15 if (input[i] !== exclude) {
16 result.push(input[i]);
17 }
18 }
19
20 return result;
21 };
22 });
We pass Peter as the single param to the exclude filter, which will render all names except Peter.
You can find the complete example on github.
Discussion
The filter implementation loops through all names and creates a result array excluding ‘Peter’. Note that the actual syntax of the filter function didn’t change at all from our previous example with the String input.
Chaining Filters together
Problem
You wish to combine several filters to form a single result.
Solution
Filters can be chained using the UNIX-like pipe syntax.
1 <body ng-app="MyApp">
2 <ul ng-init="names = ['Peter', 'Anton', 'John']">
3 <li ng-repeat="name in names | exclude:'Peter' | sortAscending ">
4 <span>{{name}}</span>
5 </li>
6 </ul>
7 </body>
Discussion
The pipe symbol (|) is used to chain multiple filters together. First we will start with the initial Array of names. After applying the exclude filter the Array contains only ['Anton', 'John'] and afterwards we will sort the names in ascending order.
I leave the implementation of the sortAscending filter as an exercise to the reader ;-)
Testing Filters
Problem
You wish to unit test your new filter. Let us start with an easy filter, which renders a checkmark depending on a boolean value.
1 <body ng-init="data = true">
2 <p>{{ data | checkmark}}</p>
3 <p>{{ !data | checkmark}}</p>
4 </body>
5
6 var app = angular.module("MyApp", []);
7
8 app.filter('checkmark', function() {
9 return function(input) {
10 return input ? '\u2713' : '\u2718';
11 };
12 });
Solution
Use the angular-seed project as a bootstrap again.
1 describe('MyApp Tabs', function() {
2 beforeEach(module('MyApp'));
3
4 describe('checkmark', function() {
5 it('should convert boolean values to unicode checkmark or cross',
6 inject(function(checkmarkFilter) {
7 expect(checkmarkFilter(true)).toBe('\u2713');
8 expect(checkmarkFilter(false)).toBe('\u2718');
9 }));
10 });
11 });
Discussion
The beforeEach loads the module and the it method injects the filter function for us. Note, that it has to be called checkmarkFilter, otherwise Angular can’t inject our filter function correctly.