In the past couple of days, I've been trying to build custom list filters using AngularJS.
Little did I know how surprisingly difficult it is to find an example close enough to what I wanted, and how simple it is to solve the problem.

I started out with some example data (json format), a list using ng-repeat, and a dialog for filtering.

data/members.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[{
    "birthday": "09-10-1940",
    "description": "Lead Singer & Song Writer",
    "name": "John Lennon"
}, {
    "birthday": "18-06-1942",
    "description": "Singer & Song Writer",
    "name": "Paul McCartney"
}, {
    "birthday": "25-02-1943",
    "description": "Lead Guitarist",
    "name": "George Harrison"
}, {
    "birthday": "07-07-1940",
    "description": "Drummer",
    "name": "Ringo Starr"
}]

html template

1
2
3
4
5
<div ng-repeat="member in members">
    <h1></h1>
    <div class="desc"></div>
    <div class="birthday"></div>
</div>

controller in controllers.js

1
2
3
4
5
6
angular.module('myApp.controllers', [])
  .controller('ListCtrl', ['$scope', '$http', function($scope, $http) {
    $http.get('data/members.json').success(function(data) {
        $scope.members = data;
    });
  }])

If you want to separate the json url from the controllers file (highly recommended) you
can look into my post about Adding Config to your AngularJS app.

All I wanted to do is grab the information from the filter dialog/form and filter the list.

The grabbing of the data part was tricky, mainly because I forgot that the filter and the list were on 2 different controllers.

After knowing what to look for (debugging with AngularJS is definitely not a walk in the park), I discovered the joy of Services.

Services are basically separate components, containing functions and data you have (potential) access to from any controller.
(My description might not be the perfect way to clarify it, but it's enough for this case).

I simply wrote a new filterService that would hold the filter information, and made sure both controllers had it in the declaration.

services.js

1
2
3
4
5
6
7
8
9
10
11
12
angular.module('myApp.services', [])
  .service('filterService', function() {
    var filter = 0;
    return {
        getFilter: function () {
            return filter;
        },
        setFilter: function(value) {
            filter = value;
        }
    }
  });

html template

1
2
3
4
5
<div ng-repeat="member in members | filter:filterService.getFilter()">
    <h1></h1>
    <div class="desc"></div>
    <div class="birthday"></div>
</div>

Okay, so now we have the data, and now we can filter the list, but what happens if the filter information is a bit more complex than that?
Lets say you have a range of dates that you want to filter, what do you do then?

Now we're creating a custom filter.
Defining a custom filter is pretty easy once you get the hang of it.
I just needed it to contain a function that would get the items (from the ng-repeat loop we have) as the first parameter, and the filter information as the second, add whatever logic I wanted (such as date difference calculation) and return the leftover items, which went something like this:

filters.js

1
2
3
4
5
<div ng-repeat="member in members | filterCustom:filterService.getFilter()">
    <h1></h1>
    <div class="desc"></div>
    <div class="birthday"></div>
</div>

html template

1
2
3
4
5
<div ng-repeat="member in members | filterCustom:filterService.getFilter()">
    <h1></h1>
    <div class="desc"></div>
    <div class="birthday"></div>
</div>

filter html template

1
2
3
4
5
6
7
8
<form id="filter-form">
  <fieldset>
    <legend>Filter</legend>
    <label>Birthday max day of the week:</label>
    <input ng-model="filter.max_day_of_week" type="text" placeholder="Leave Empty to show all">
    <button type="submit" class="btn" ng-click="close(filter)">Submit</button>
  </fieldset>
</form>

filters.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
angular.module('myApp.filters', [])
  .filter('filterCustom', [function() {
    return function(items,filter) {
      var dayOfWeek = function(item) {
          var t = item.birthday.getDay(); // day of the week
          return day_diff <= filter.max_day_of_week;
      };
      if (filter.max_day_of_week) {
        items = items.filter(dayOfWeek);
      }
      if (filter.order) {
        items = items.sort(function(a,b) { return b[filter.order] - a[filter.order] })
      }
      return items;
    }
  }]) ;

On this example, I've filtered by Day of the Week (which shouldn't be more than the number entered in the form.. silly, I know)

Very important is to make sure that myApp.filters and myApp.services are added in app.js