Angular Filter Checkboxes

Home / Angular Filter Checkboxes

A fellow developer asked me how filter Pipes could be used for filtering based on checkboxes. Using the existing code samples I’ve created, I endeavored to put together a little demo of how this can be achieved.


Angular’s custom Pipes can, really, achieve nearly any filtering you’d want. The idea of filtering with checkboxes means that you have discrete values associated with the checkboxes, when checked, and you want to compare those checked values against properties within an array of objects. Sounds straight-forward, right?

The first hurdle is handling checkboxes in Angular. I decided to use an ngFor, and various bindings, for this purpose to display a list of checkboxes.

<div class="form form-inline">
  <div *ngFor="let filterItem of filterItems; let i = index;" class="form-check" style="padding-right:20px;">
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" [(ngModel)]="filterItem.checked" [value]="filterItem.value"> Value {{ i + 1 }}
    </label>
  </div>
</div>

<pre>Selected items: <span *ngFor="let filterItem of checked()" style="padding-right:10px;">{{ filterItem.value }}</span></pre>

You can see I’m using Angular Forms’ ngModel to find the checked property to my “filterItem” and mapping the [value] attribute to the filterItem’s value property. The attribute could be left out since all I’m really interested in knowing is whether the checkbox is checked.

I’m also using an ngFor to display which filterItems are checked. I call a simple “checked” method that returns the checked filterItems. This uses the simple Array.prototype.filter method.

checked() {
  return this.filterItems.filter(item => { return item.checked; });
}

The final part of the HTML mark-up is to display the list of items that is being filtered.

<ul>
  <li *ngFor="let item of items | filter: { property: ''} : checked() : false">{{ item.name }} - {{ item.property }}</li>
</ul>

You can see the filter signature in the mark-up. I’m passing in an object that will contain the filter keys, the array of checked items, and a boolean indicating whether AND filtering is applied. Using “and” probably doesn’t make a lot of sense unless there are multiple property keys with only a single checked item. Having multiple checked items with only a single property, obviously, would always return an empty filtered list.

The filter itself doesn’t deviate much from the other filters I’ve written. The only major difference is that the checked array has to be reduced and the “checked.value” is compared against each item’s property key values.

@Pipe({
    name: 'filter'
})
export class FilterPipe implements PipeTransform {
  transform(items: any, filter: any, filterItems: Array<any>, isAnd: bool): any {
    console.log('Filtering ..');
    if (filter && Array.isArray(items) && filterItems) {
      let filterKeys = Object.keys(filter);
      let checkedItems = filterItems.filter(item => { return item.checked; });
      if (!checkedItems || checkedItems.length === 0) { return items; }
      if (isAnd) {
        return items.filter(item =>
            filterKeys.reduce((acc1, keyName) =>
                (acc1 && checkedItems.reduce((acc2, checkedItem) => acc2 && new RegExp(item[keyName], 'gi').test(checkedItem.value) || checkedItem.value === "", true))
              , true)
              );
      } else {
        return items.filter(item => {
          return filterKeys.some((keyName) => {
            return checkedItems.some((checkedItem) => {
              return new RegExp(item[keyName], 'gi').test(checkedItem.value) || checkedItem.value === "";
            });
          });
        });
      }
    } else {
      return items;
    }
  }
}

The Pipe has, like I mentioned, the additional some/reduce of the checked items. This is used to compare each key specified against the value of the checked item using simple regular expressions.

The plunk below shows everything in action with a default “OR” matching. By default, if the checked items is empty, then all items are shown.

Leave a Reply