How to filter results list in Angular with input text

Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repo on Github - Star

I find myself lately looking for recent visited bookmarks via the Ctrl+h - History dialog on To make my life even easier, I added a filter box in the dialog. You can now add one or more keywords to filter the displayed results even further. One thing let to another and I have added the filter box to other bookmarks lists, like Pinned, ReadLater or My Dashboard:

show filter animation

In this blog post I will present the implementation in Angular required to achieve this new feature.

Octocat Source code for is available on Github - Star

The bookmarks filter pipe code

The easiest way to achieve the filtering functionality is to use an angular pipe1:

<mat-expansion-panel *ngFor="let bookmark of bookmarks | bookmarkFilter: filterText">

The complete implementation of the pipe is:

// bookmarks-filter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { Bookmark } from '../core/model/bookmark';

@Pipe({name: 'bookmarkFilter'})
export class BookmarksFilterPipe implements PipeTransform {
   * Bookmarks in, bookmarks out that contain all the terms in the filterText
   * @param {Bookmark[]} bookmarks
   * @param {string} filterText
   * @returns {Bookmark[]}
  transform(bookmarks: Bookmark[], filterText: string): Bookmark[] {
    if (!bookmarks) {
      return [];
    if (!filterText) {
      return bookmarks;

    return bookmarks.filter(bookmark => {
      return this.bookmarkContainsFilterText(bookmark, filterText);

  private bookmarkContainsFilterText(bookmark: Bookmark, filterText): boolean {
    filterText = filterText.toLocaleLowerCase();
    const filterTerms = filterText.split(' ');
    for (const filterTerm of filterTerms) {
      const hasFilterTerm = this.bookmarkContainsFilterTerm(bookmark, filterTerm);
      if (hasFilterTerm === false) {
        return false;

    return true;

  private tagsHaveFilterText(tags: string[], filterText: string): boolean {
    for (const tag of tags) {
      if (tag.includes(filterText)) {
        return true;

    return false;

  private bookmarkContainsFilterTerm(bookmark: Bookmark, filterTerm: string) {
      || bookmark.location.toLocaleLowerCase().includes(filterTerm)
      || bookmark.description.toLocaleLowerCase().includes(filterTerm)
      || this.tagsHaveFilterText(bookmark.tags, filterTerm);

It checks if the bookmarks contains all the filter terms provided in the filterText either in title, location, tags or description of the bookmark.

The filterText it’s split into terms by spaces and all terms must be present

Usage in the Angular History Dialog Component

Below is the complete usage of the bookmarkFilter in the history dialog html component:

<div class="dialog-title">
  <h2 mat-dialog-title [innerHTML]="title"></h2>
  <div class="form-group has-search">
    <span class="fas fa-filter form-control-feedback"></span>
    <input type="search" [(ngModel)]="filterText" class="form-control" placeholder="Filter...">
<mat-dialog-content *ngIf="(bookmarks$ | async) as bookmarks" class="mt-2 pt-1 pb-1">
    <mat-expansion-panel *ngFor="let bookmark of bookmarks | bookmarkFilter: filterText">
        <div class="p-3">
          <h5 class="card-title">
            <a href=""
               [innerHTML]=" | slice:0:100 | highlightHtml: filterText"
               (click)="addToHistoryService.promoteInHistoryIfLoggedIn(true, bookmark)"
               (auxclick)="addToHistoryService.onMiddleClickInDescription(true, $event, bookmark)"
              see innerhtml
            <sup class="external-link-hint"><i class="fas fa-external-link-alt"></i></sup>
          <h6 class="card-subtitle mb-2 text-muted url-under-title"
              [innerHTML]="bookmark.location | slice:0:120 | highlightHtml: filterText"
            see innerhtml

      <ng-template matExpansionPanelContent>
        <app-bookmark-text [bookmark]="bookmark"
                           (click)="addToHistoryService.onClickInDescription(true, $event, bookmark)"
                           (auxclick)="addToHistoryService.onMiddleClickInDescription(true, $event, bookmark)">

The filterText variable is a two-way bounded variable - <input type="search" [(ngModel)]="filterText" class="form-control" placeholder="Filter...">. The value of the html input is filter parameter filter of the bookmark filter pipeline as seen before - transform(bookmarks: Bookmark[], filterText: string): Bookmark[].

Note the highlight pipe chained to highlight the search terms: [innerHTML]=" | slice:0:100 | highlightHtml: filterText"

In the component filterText is defined as a simple string variable:

export class HotKeysDialogComponent implements OnInit {

  bookmarks$: Observable<Bookmark[]>;
  title: string;
  filterText: '';

    private dialogRef: MatDialogRef<HotKeysDialogComponent>,
    public addToHistoryService: AddToHistoryService,
    @Inject(MAT_DIALOG_DATA) data
  ) {
    this.bookmarks$ = data.bookmarks$;
    this.title = data.title;

  ngOnInit() {

Bonus: the Highlight Pipe

You can find below the implementation of the Highlight pipe, which highlights the filter terms in the history dialog:

import {Pipe} from '@angular/core';
import {PipeTransform} from '@angular/core';

@Pipe({ name: 'highlightHtml' })
export class HighLightHtmlPipe implements PipeTransform {

  transform(text: string, search): string {
    if (!search || search === undefined) {
      return text;
    } else {
      let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
      pattern = pattern.split(' ').filter((t) => {
        return t.length > 0;
      pattern = '(' + pattern + ')' + '(?![^<]*>)';
      const regex = new RegExp(pattern, 'gi');

      return search ? text.replace(regex, (match) => `<span class="highlight">${match}</span>`) : text;



In this post you’ve seen a way to dynamically filter a list of elements in Angular with the helps of pipes.

If you liked this, please show some love and give us a star Star


Subscribe to our newsletter for more code resources and news

Adrian Matei

Adrian Matei
Life force expressing itself as a coding capable human being

How to display an element in full screen in Angular with screenfull.js

Use screenfull.js to display code snippets in full screen in Angular. Continue reading

Map enum as string with jpa

Published on June 18, 2021

How to set createdAt date with jpa

Published on June 18, 2021