Building Custom AngularJS Filters

Not too long ago we took a look at some of the built-in Angular filters. The built-in filters cover many common use cases including formatting dates, currencies, limiting the number of items displayed and more. These filters are both useful and give insights into how we may improve our workflow when building Angular apps.

Today, we will build our own custom AngularJS filters. We'll start simple and build a couple filters that manipulate numbers and strings, then we'll build a filter that manipulate an entire data set. Finally, in our previous article where we discussed the built in AngularJS filters, Pierre-Adrien asked how we could use the built in Angular currency filter to display the currency denomination after the amount (i.e. 9.99$ instead of $9.99) as is common in some places of the world. Unfortunately, the built-in currency filter does not support this functionality, so we'll build our own that does!

Anatomy of an Angular Filter

Angular exposes a simple API for creating a filter. Just as you would declare a controller with app.controller(‘myCtrl', function(){});, you can create a new filter by appending .filter(‘filterName', function(){}) to your Angular app.

A filter is very similar to an factory or service in many regards but has the added advantage of behaving on a global scope once created. As we have previously seen, you can invoke a filter on both the data binding in your html or directly inside of your controller or directive by using the $filter service. Let's break down the structure of a filter.

// To declare a filter we pass in two parameters to app.filter
// The first parameter is the name of the filter
// second is a function that will return another function that does the actual work of the filter
app.filter('myFilter', function() {
// In the return function, we must pass in a single parameter which will be the data we will work on.
// We have the ability to support multiple other parameters that can be passed into the filter optionally
return function(input, optional1, optional2) {
var output;
// Do filter work here
return output;
}
});

This may seem confusing from the get go, so let's jump to some examples that will demystify writing custom filters.

Our First Custom Filter

Let's start off slow and simple. The first custom filter we'll write will convert numbers to their ordinal values, meaning that if we apply our ordinal filter to say the number 43, what will be displayed is "43rd". Let's look at the code for our ordinal filter.

// Setup the filter
app.filter('ordinal', function() {
// Create the return function
// set the required parameter name to **number**
return function(number) {
// Ensure that the passed in data is a number
if(isNaN(number) || number < 1) {
// If the data is not a number or is less than one (thus not having a cardinal value) return it unmodified.
return number;
} else {
// If the data we are applying the filter to is a number, perform the actions to check it's ordinal suffix and apply it.
var lastDigit = number % 10;
if(lastDigit === 1) {
return number + 'st'
} else if(lastDigit === 2) {
return number + 'nd'
} else if (lastDigit === 3) {
return number + 'rd'
} else if (lastDigit > 3) {
return number + 'th'
}
}
}
});

Applying this filter to our views is straightforward:

{{ 25 | ordinal }}

will yield 25th. If we were to apply the ordinal filter to a string, such as

{{ 'not a number lol' | ordinal }}

we would simply get the string not a number lol back.

It is a good practice to ensure you have appropriate data to filter, and if you do not simply return the unmodified data back. Take a look at the CodePen below for some additional examples.

Capitalizing on Custom Filters

Sorry for the bad joke. The next custom filter we build will capitalize either the first letter or a letter we specify. The additional parameter will specify which letter to capitalize, if no additional parameter is passed than the first letter will be capitalized.

This is a bit of a contrived example and has no real practical uses but we'll use it to show off how you could extend your filters.

Filters That Actually Filter

In the previous examples, we applied filters to single items, now let's apply a filter to a collection. In this example, we will actually filter a data set.

In programming, there are hundreds of ways to reach the end goal, and in this example what we'll filter a list and return only the items that match a certain criteria.

We will go through a list of programming languages and display only the statically typed ones. Easy enough right?

// Setup the filter
app.filter('staticLanguage', function() {
// Create the return function and set the required parameter name to **input**
return function(input) {
var out = [];
// Using the angular.forEach method, go through the array of data and perform the operation of figuring out if the language is statically or dynamically typed.
angular.forEach(input, function(language) {
if (language.type === 'static') {
out.push(language)
}
})
return out;
}
});

Custom Currencies

In the first example, we looked at creating a simple custom filter that only did one thing (and hopefully did that one thing well). Next, let's take a look at how we can create a filter that accepts additional parameters.

The idea for this filter comes from Pierre Adrian who was wondering whether the built-in currency filter supports the ability to choose what side the currency symbol goes on. Unfortunately, it does not, so we'll build our own custom currency filter that does!

In the US it is standard practice to place the $ symbol before the amount (i.e. $9.99), but in certain countries it is customary to place the symbol after the amount (i.e 9.99$).

For our custom filter, we will allow the user to pass two parameters. The first will be the symbol or string they want to use to denote the currency, and second a true or false boolean value that will determine whether the symbol is added before or after the amount.

We will default the symbol to the dollar sign ($) and the position to before of the amount so that if those aren't passed the filter still works.

// Setup the filter
app.filter('customCurrency', function() {
// Create the return function and set the required parameter name to **input**
// setup optional parameters for the currency symbol and location (left or right of the amount)
return function(input, symbol, place) {
// Ensure that we are working with a number
if(isNaN(input)) {
return input;
} else {
// Check if optional parameters are passed, if not, use the defaults
var symbol = symbol || '$';
var place = place === undefined ? true : place;
// Perform the operation to set the symbol in the right location
if( place === true) {
return symbol + input;
} else {
return input + symbol;
}
}
}
});

One thing to note when dealing with filters that support multiple parameters: you must pass the parameters in the correct order! You do not have to pass all the parameters, so in our custom currency filter it is perfectly acceptable to pass only the symbol, but you cannot only pass the location of where you want the symbol to display.

If you wanted to change only the order, you would still need to pass in the symbol such as {{ 25 | customCurrency:'$':false }}.

Conclusion

Today we built our own custom AngularJS filters. We learned how to create filters from scratch, built filters that did single tasks and created filters that had extended functionality. Filters can be a powerful tool for extending the presentation of your applications. What are some custom filters you would like to see?