When building web applications using grid or list views, we often have the need to filter the data based on a selected option. With AngularJS this is a trivial task as we can use a filter to select the data, but it gets tricky when we need to provide the same functionality using multiple filter options. With this article, we take a look at implementing a multi-option filter using anonymous functions.
Defining the model
For our demo app, we build a simple vehicle inventory viewer which can filter results by selecting one or more makes. The vehicles associated to our checked selections should be displayed.
var list = [
{ "id": 1, "year": 2018, "make": "Nissan", "model": "Altima" },
{ "id": 2, "year": 2018, "make": "Nissan", "model": "XTerra" },
{ "id": 3, "year": 2018, "make": "Subaru", "model": "Outback" },
{ "id": 4, "year": 2018, "make": "Subaru", "model": "Crosstrek" },
{ "id": 5, "year": 2018, "make": "Toyota", "model": "4Runner" },
{ "id": 6, "year": 2018, "make": "Toyota", "model": "Corolla" }
];
|
Notice on our model that there multiple items with the same make, so our first step is to get a list of unique makes. This can be done by creating a hash table with the make property name as the key. We also add a checked property which can be bound (ngModel) to the checkbox to track what filters are selected.
ctrl.makes = {};
//build the list of makes using a hash
list.filter(function (value, index, arr) {
if (!ctrl.makes[value.make]) {
ctrl.makes[value.make] = { make: value.make, checked: true };
}
});
|
Anonymous Function
The last step on the code is to build the filter logic. This can be done using an anonymous function which can be piped as a filter on the ngRepeat directive. The function gets each item on the collection via the item parameter. The item.make property is used as a key to get the object from the makes collection and validate if the item checked property is checked. A true value means that the item meets the filter condition, and it is displayed.
ctrl.optionFilter = function () {
return function (item) {
return ctrl.makes[item.make].checked;
};
};
|
Building the Views
We can now move forward with building our views. We need the checkboxes for the filter options as well as a grid for the inventory list. We can build those controls with the following steps:
Filter Options
We use our makes collection of objects to create the checkboxes using the ngRepeat directive. For the filter to work properly, we need to watch over the checked property by associating it to the ngModel directive.
<span class="center-block">
<label data-ng-repeat="option in ctrl.makes">
<input type="checkbox"
ng-model="option.checked"
ng-checked="option.checked"/> {{option.make}}
</label>
</span>
|
Table / Grid
To build the grid, we use the ngRepeat directive on our list collection. If we look at the ngRepeat directive, we can see that we pipe the items to our anonymous function which is called for each item. This is needed to check if the corresponding make is checked. We also alias the result, so we can display the count of items.
<table class="table table-bordered text-center">
<tr ng-repeat=" item in ctrl.list | filter: ctrl.optionFilter() as result" >
<td><i class="fa fa-car fa-2x text-primary"> </i></td>
<td>{{item.year}}</td>
<td>{{item.make}} </td>
<td>{{item.model}}</td>
</tr>
</table>
|
In Action
Summary
We are able to see how to filter a list with multiple options using our old friend AngularJS filter and an anonymous function. This however is not an acceptable solution when using the new Angular generation of software as filters have been a major cause of performance issues when dealing with large datasets. We can learn more about that here Angular Documentation.
In our next article, we see how to implement the same solution using Angular 5 with Typescript and Observers (Reactive Programming) which should address those performance concerns.
Check this article for the Angular, Typescript Reactive Version