Building Custom AngularJS Filters

In recent months I’ve really started to get excited about Angular filters! I know – living the dream right?

Straight out of the box, Angular provides some pretty cool filters such as orderBy which lets you order an array or list, date which lets you format a date, and json which displays any JSON data in a nice format (great for debugging your Angular application). Plus (as with virtually everything else to do with Angular) if you need something that doesn’t come straight out of the box, you can just build your own custom filter and do it yourself!

Here’s a little story about a time (this afternoon actually) when an off-the-shelf filter just didn’t quite do what I wanted it to, so I knocked up a quick custom filter which did exactly what I wanted it to do. I was very happy about that, and wanted to share the love!

In the project I’m working on at the minute, I wanted to format a date so it displayed the ordinal (the “th” or “rd” or “nd” that comes after the numerical day part of a date) on a report, and the AngularJS date filter just didn’t do it for me.

To recap, the Angular date filter works like this.

If you have one of those horrible JSON-formatted dates like 2016-02-25T22:04Z and you want to transform it into something that humans can make sense of without their eyes starting to bleed, you can either write a custom JavaScript function to strip it apart and rearrange it into the correct format, or you can use the Angular date filter.

To use the Angular date filter to display today’s date in a nice format (e.g. 25th Feb 2016 22:04) you just drop this snippet in your template, using whichever date format you want:

{{myNastyJsonDate | date: "dd MM yyyy hh:mm"}}

However, the Angular date filter doesn’t add the “th” bit for you, so in order to do this you have to create a custom filter!

To use custom filters you have to remember 3 things –

  1. How to code a custom filter
  2. Inject the custom filter in your Angular application
  3. Use the custom filter in your markup

Ok so number 3 shouldn’t really be that hard, and number 2 can be done like this:

var myAngularApp = angular.module("angularApp", ["myCustomDatefilter"])

So that leaves us with number 1, which isn’t that tricky really – here’s the basics of creating a custom filter:

angular.module("myCustomDateFilter", [])
  .filter("formatAsLongDateTime", [
    "$filter", function($filter) {
      return function(input) {
        //your code goes here
        return ouput;
      }
    }
  ]);

So you can see from the code above – we’re creating an Angular module, which is why we have to inject our custom filter directly into our Angular application – it’s an external module that the application needs to be aware of in order to use it. This is different to most other parts of an Angular application (controllers, services, resources etc) are created within the context of the application, so you don’t need to worry about injecting them into the application.

So, we create an Angular module, and then (on line 2) we’re creating a Filter for that module. This is done in pretty much the same way you create controllers or services – give it a name, and then pass in an array of dependencies (stuff that Angular needs to inject into your filter for you in order for you to be able to use them) followed by the filter function itself.

The $filter service we’re injecting into our custom filter is the off-the-shelf Angular service that does all the basic funky stuff for us. We’re going to use that in the filter, but you don’t necessarily need to use it. Our filter function essentially needs to apply the off-the-shelf date filter, but add the “th” after the day digits.

The filter function has an input (the data you want to apply the filter to), and then you return an output (the filtered data).

The code in our filter function will look like this:

var dayPart = $filter("date")(input, "dd");
var monthYearPart = $filter("date")(input, "MMM yyyy");
var output = parseInt(dayPart, 10) + getOrdinal(dayPart) + " " + monthYearPart;
return output;

You can see we’re using the $filter service in a similar way to how we used it in the basic template earlier – we’re using the “date” method of the $filter service, passing in our input date and applying a format to it (first we’re formatting it to get the day digits, then we’re formatting it to get the month and year digits).

The final part of this is to call the “getOrdinal()” function to work out what letters to put after the day digits.

(Psst I have to admit, I did grab the code to work out the ordinal stuff from this Stack Overflow post, because why make your brain hurt when someone else has already gone through the pain!)

The code for this getOrdinal() function looks like this:

var getOrdinal = function (dayPart) {
  var suffixes = ["th", "st", "nd", "rd"];
  var day = parseInt(dayPart, 10);
  var relevantDigits = (day < 30) ? day % 20 : day % 30;
  var ordinal = (relevantDigits <= 3) ? suffixes[relevantDigits] : suffixes[0];
  return ordinal;
};

So putting it all together, it looks something like this:

angular.module("myCustomDateFilter", [])
  .filter("formatAsLongDateTime", [
    "$filter", function($filter) {
      var getOrdinal = function (dayPart) {
        var suffixes = ["th", "st", "nd", "rd"];
        var day = parseInt(dayPart, 10);
        var relevantDigits = (day < 30) ? day % 20 : day % 30;
        var ordinal = (relevantDigits <= 3) ? suffixes[relevantDigits] : suffixes[0];
        return ordinal;

       return function(input) {
         var dayPart = $filter("date")(input, "dd");
         var monthYearPart = $filter("date")(input, "MMM yyyy");
         var output = parseInt(dayPart, 10) + getOrdinal(dayPart) + " " + monthYearPart;
         return output;
       }
     }
   ]);

Finally you need to drop the custom filter into your template. It looks pretty much identical to the very first example in this article, but instead of using the standard "date" filter we use the custom filter, and we don't have to specify the date format because that's taken care of inside the custom filter code:

{{myUglyJsonDate | formatAsLongDateTime}}

You can put all your filters into one filter module if you like, by chaining another ".filter(..." to the end of the first filter constructor. I prefer to keep them in separate modules though if they're not related (i.e. I have one module for my date/time formatting filters, one for my list filtering filters etc)

You can make them as simple (like this example) or as complex as you want (or need to) - which can be fun or not so much fun, depending on how you view the world...

Posted in AngularJS, Code, Code samples, Front-end Tagged with: , , ,
  • Rob Horton

    Nice job Maff! I always enjoy your Angular posts! This was very well done.

  • deepak

    Maffy I am having problem with angularjs local storage. after setting
    the token in storage at first time its need to refresh page to get the
    token, basically I am using auth token in every http request for that after login I am storing the token in local storage and also setting it into http request object to so in every request the token will be pass but after refreshing the page the request object loose the token, for that I set the same token in app.js onload method also but not working as expected, so can you please help where I am wrong.

  • This was a good read! Thank you for sharing your thoughts. I found this interesting article that may be helpful , check it out http://goo.gl/Hb5Rku.

About Maff

Maff Rigby

I'm a certified .Net, Umbraco and AngularJS freelance developer with over 15 years experience in the IT industry. As well as writing code I love to teach; I run a number of workshops and 1-1 coaching sessions on Angular JS and Umbraco, and share what I know and learn here!

I’m social (ish)

Connect with me on LinkedIn, follow me on Twitter, or fail to find me on Facebook.