Angular filters work in pipeline fashion similar to commands in a Unix shell. The Angular syntax borrows the vertical bar pipe symbol (|) from Unix, which you use to pass an initial expression or scope property to a filter as well as to chain filters together.

Since we're going to create a few HTML tables later in this section, let's load the Bootstrap CSS framework from a content delivery network for some helpful styling.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>

In addition, the later examples will use a few simple controllers and services to prepare data for the view, so we'll declare a root module to manage them.

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

The argument to ng-app is the name of our application's root module.

<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body>

Filters are not a difficult concept to master, so this chapter will be a fast-paced tour through the built-in filters provided by Angular, followed by a demonstration of how to create a custom filter of your own.

lowercase and uppercase

Let's start our tour of Angular's built-in filters with some string transformations. The lowercase filter is a perfect introduction. We start with any expression ("AngularJS" in this example), followed by the pipe symbol (|), and the name of the filter (lowercase).

<p>{{"AngularJS" | lowercase}}</p>

We can apply the uppercase filter to the result by adding another pipe and filter, forming a chain.

<p>{{"AngularJS" | lowercase | uppercase }}</p>

It doesn't make much sense to chain lowercase and uppercase, of course. Later in the chapter we'll see a better example of chaining, but at least now you've seen it done.

number

This next one is also easy. The number filter provides some basic assistance with formatting and rounding numbers.

<p>{{1000.12345 | number}}</p>

In my locale, en-us, the output of this example is 1,000.123. The filter rounds the number argument to 3 decimal places. Clearly this default will not work for every case. It probably won't work for even a majority of use cases. Fortunately, you can pass arguments to filters that accept additional parameters, as number does. The first parameter declared by a filter is the argument that you pipe in, and the additional arguments follow the filter name, separated from it by a colon (:).

The second parameter of number is fractionSize. Happily, it lets us control how many digits are displayed past the decimal point, so that we can now see our complete value.

<p>{{1000.12345 | number:5}}</p>

What happens if you pass a negative value for fractionSize? Replace 5 with -3, then with -4 in the example above. Can you guess what's happening? That's all right, neither can I.

currency

This next filter is quite useful in real-life applications: currency provides basic assistance with formatting and rounding numbers.

<p>{{1999.995 | currency}}</p>

The currency filter accepts two additional parameters. The first is a currency symbol. Replace currency with currency:'€' in the example above to try it.

The second parameter is fractionSize. When using currency, be careful about rounding monetary values for display. There is no single correct way to round, in the sense that rounding policy is a business decision, not a technical one. The filter's use of the round half up method is not always ideal, as shown in this example. Since the currency filter does not currently offer a documented way to override its rounding method, can you modify the example to at least show the true price?

date

The date filter shows just how far you can go with filter configuration.

Initializing a Date object is not allowed within an Angular expression, so we will use a controller to prepare the model for this example.

angular.module('app')
  .controller('DateController', function($scope) {
    $scope.now = Date.now();
  });

This example below begins with a property on the scope called now, rather than an expression.

<p ng-controller="DateController"> {{now | date}} </p>

Remove the filter by deleting | date from the example above. You should see the raw value of now, which is the milliseconds since January 1, 1970, UTC. Now, try out a couple of the predefined formats supplied by the filter. Paste in | date:'medium' and then | date:'shortTime'. You can also paste in | date:'yyyy-MM-dd HH:mm:ss Z' and experiment a little on your own with the formatting. String literals are allowed in the format pattern, for something like | date:"MMMM 'of the year' yyyy". Instead of using an escape character such as % for the formatting symbols, you need to enclose your string literals in single quotes within a double-quoted formatting string. Spaces in the formatting pattern are preserved.

orderBy

Filters can do more than just format output. They can also be used to transform collections, as we will see in the upcoming examples.

Imagine that a backend service returns records in an order that is undesirable for display purposes. (For the sake of simplicity in this example, our data is actually just hardcoded inside the items service shown below, rather than retrieved from a real remote backend.)

angular.module('app')
  .value('items', [
    {name: 'Item 3', position: 3, color: 'red', price: 2.75},
    {name: 'Item 1', position: 1, color: 'red', price: 0.92},
    {name: 'Item 4', position: 4, color: 'blue', price: 3.09},
    {name: 'Item 2', position: 2, color: 'red', price: 1.95}
  ]);

We then inject this items array into a controller.

angular.module('app')
  .controller('ItemsController', function($scope, items) {
    $scope.items = items;
  });

Given the current ordering of the array, how do we display the items sorted by position?

If you just jumped out of your seat, shouting "Underscore!" (or "lodash!"), I award you the clued-in JavaScripter badge, but it is not the answer I am looking for. Angular has a filter for that! It is orderBy.

<ol ng-controller="ItemsController"> <li ng-repeat="item in items | orderBy:'position'"> {{item.name}} </li> </ol>

This is great, but what if we need to sort in reverse order? In this simple example, there are actually two ways to do it. The first way is to prepend a - symbol before the name of the property. In the example, replace orderBy:'position' with orderBy:'-position'. (You can also prepend a + symbol to specify ascending order, although it should have no effect on the original example.) The second approach is to append a reverse boolean argument after the name of the property. In the example, replace orderBy:'position' with orderBy:'position':true.

Extra credit: Edit the records in the controller example so that two of the records have the same value for position. Can you still use orderBy to sort the records correctly? Which property did you use?

Beware of updates

With Angular's two-way data binding, allowing your user to update models inside a list or table view is possibly too easy. All it takes is adding an input within the loop with an ng-model parameter set to the name of the collection element. But look out! Since Angular is always ready and waiting to redraw the view on state changes, you may find your list resorted before you are even finished editing.

<table class="table table-condensed" ng-controller="ItemsController">
  <tr ng-repeat="item in items | orderBy:'name'">
    <td ng-bind="item.name"></td>
    <td><input type="text" ng-model="item.name"></td>
  </tr>
</table>

Try editing the name of one of these records by first deleting, then adding a character, and you will notice a major usability issue. Angular will redraw the entire table, moving your row to a new position, before you are done editing! The best solution to this issue may be to use a separate view for editing. On the other hand, you can also move the orderBy filter from the view to the controller so that it is only invoked once, as explained in the upcoming section, Using filters in JavaScript.

limitTo

The limitTo filter is helpful when you want to show only a subset of an ordered collection. For example, we can use it in cooperation with orderBy to display only the two most expensive items.

<ol ng-controller="ItemsController"> <li ng-repeat="item in items | orderBy:'-price' | limitTo:2"> {{item.price | currency}} </li> </ol>

To support pagination, the limitTo filter declares an optional parameter, begin. To see the second page of results in the example above, simply add a second argument:limitTo:2:2.

filter

Somewhat humorously, there is an Angular filter named filter. While the general name filter may be a poor choice (perhaps decorators or maybe helpers would be better), the filter that is named filter actually does what its name says. It returns a new array containing only elements that match the supplied predicate. Let's see what matches when we use the numeric value 3 as a predicate.

<table class="table table-condensed" ng-controller="ItemsController">
  <thead>
    <tr><th>name</th><th>color</th><th>price</th></tr>
  <thead>
  <tbody>
    <tr ng-repeat="item in items | filter:3">
      <td ng-bind="item.name"></td>
      <td ng-bind="item.color"></td>
      <td ng-bind="item.price | currency"></td>
    </tr>
  <tbody>
</table>

We get matches on both the name and price properties. What happens if you replace the numeric 3 predicate with a string '3'? Or with an empty string? Or if you add a whitespace character in front (' 3')?

To avoid matching on any property, you can also pass an object as predicate.

<table class="table table-condensed" ng-controller="ItemsController">
  <thead>
    <tr><th>name</th><th>color</th><th>price</th></tr>
  <thead>
  <tbody>
    <tr ng-repeat="item in items | filter:{price: 2, color: 'red'}">
      <td ng-bind="item.name"></td>
      <td ng-bind="item.color"></td>
      <td ng-bind="item.price | currency"></td>
    </tr>
  <tbody>
</table>

The example above avoids matching the record with 2 in its name.

If there is anything surprising about filter, it is just how astoundingly easy it is to combine with Angular's two-way binding for a powerful filter box.

Filter: <input type="text" ng-model="q">
<table class="table table-condensed" ng-controller="ItemsController">
  <thead>
    <tr><th>name</th><th>color</th><th>price</th></tr>
  <thead>
  <tbody>
    <tr ng-repeat="item in items | filter:q">
      <td ng-bind="item.name"></td>
      <td ng-bind="item.color"></td>
      <td ng-bind="item.price | currency"></td>
    </tr>
  <tbody>
</table>

If there is one example that best demonstrates the supple power of Angular's built-in extensions to HTML, this may be it.

Using filters in JavaScript

Sometimes you want to do something with a filter where you have the full power of JavaScript, rather than be limited by the constraints of an expression. For example, something as simple as reading the length of a filter result doesn't seem to be possible. You can also argue that when expressions begin to acquire logic and grow more verbose, it's best to move as much of that code as you can into a controller.

<p ng-controller="ItemsController"> <!-- Invalid code! --> {{items | filter:{color:'red'}.length}} red items </p>

You can inject filters anywhere you want, and in fact it's not uncommon to use the filter filter to manipulate a collection from within a controller.

The provider for a filter will be named <filtername>Filter. For example, the date filter is injected with dateFilter, the currency filter with currencyFilter, and the filter filter with filterFilter. (I know, I know!)

angular.module('app')
  .controller('FilteredItemsController', function($scope, items, filterFilter) {
    $scope.redItemsCount = filterFilter(items, {color: 'red'}).length;
  });

<p ng-controller="FilteredItemsController"> {{redItemsCount}} red items </p>

The main thing to keep in mind about using a filter in a controller rather than in a view is that it will not be invoked during UI updates that do not involve reloading the controller. The solution to this issue is to manually bind the filter usage to a scope property using scope.$watch.

Custom filters

A custom filter is the best place to put code that transforms model data just for the view, because a filter is typically a stateless component that can be used anywhere in your templates without fear of side effects. Creating custom filters of your own is not difficult. Like other Angular components, you register a filter recipe on an Angular module, using the filter function.

angular.module('app')
  .filter('smiley', function() {
    return function(text) {
      return '\u263A ' + text + ' \u263A';
    };
  });

In the example above, the actual filter is the inner function that concatenates its text parameter with smiling emoticons. This function is invoked every time you use the filter. The outer function that encloses it is a creation recipe, a topic covered in greater depth in the previous chapter, Services. The recipe is run once or never, depending on whether you actually use the filter in a view.

Custom filters are automatically available everywhere in your views, and are used exactly the same as built-in filters. You simply pipe any expression in your view into the filter, using the pipe symbol (|), and pass additional arguments using the colon separator (:).

<strong ng-bind="'hello' | smiley"></strong>

Here's a challenge: Can you refactor the filter and its usage so that the emoticon to be passed in as an argument?

Dependency injection

Although filters are typically stateless and usually fairly simple, they can have dependencies that need to be injected.

The outer creation recipe function that wraps the filter function allows you to declare dependencies just as you would in a controller. Let's take a look at how we might inject dependencies into a custom filter for localization. We'll name the filter localize. But first of all, we need something to inject. We can create a map of locales data, registered as an injectable service using the value function.

angular.module('app')
  .value('locales', {
    'de': {greeting: 'guten Tag'},
    'en-us': {greeting: 'howdy'},
    'fr': {greeting: 'bonjour'}
  });

We can inject this data into our custom filter simply by declaring a parameter named locales. In order to get the user's current locale as a code, we can also inject Angular's built-in $locale service.

angular.module('app')
  .filter('localize', function(locales, $locale) {
    return function(key) {
      var locale = locales[$locale.id] || locales['en-us'];
      return locale[key];
    };
  });

This example should work out of the box for readers with one of the provided locales. If your current locale is not included, just add it to the map in the example with a friendly greeting in your own language.

<p>
  The app says <strong ng-bind="'greeting' | localize"></strong>.
</p>

You can also inject filters into your custom filters, of course. Moving complex arguments and lengthy filter chains into a custom filter is a great way to refactor some of the noise out of your views.

Conclusion

Angular's built-in filters are an important part of its impressive out-of-the-box capability. They are generally intuitive and easy to use, as well as flexible thanks to additional arguments and chaining. Creating custom filters is also easy once you get the hang of the creation recipe pattern that allows you to inject dependencies. Now that we have some familiarity with using this pattern, we are better prepared for the challenges of the next chapter, in which we tackle writing our own custom directives.

I hope you have been enjoying Angular Basics.
You can join the mailing list to hear about updates to the book.
Please also let me know what you liked and what I can improve.
And please share the word using the social buttons below!