Sitemap

Filter using Custom directives in Angular

2 min readJan 22, 2026

--

Filtering lists is something we all end up doing in apps. Sure, you can throw in a pipe or hack something together, but I wanted a reusable way that feels clean. So I wrote a simple directive.

Press enter or click to view image in full size
Filter in Angular using custom directive

Directive

import { Directive, Input, EventEmitter, Output } from '@angular/core';

@Directive({
selector: '[appFilter]',
})
export class FilterDirective<T> {
@Input('appFilter') items: T[] = [];
@Input() filterKey?: string;
@Output() filtered = new EventEmitter<T[]>();

private query: string = '';

ngOnChanges() {
this.query = this.filterKey?.toLowerCase() || '';
this.applyFilter();
}

private applyFilter() {
console.log('Filtering with query:', this.query);
if (!this.items) return;
if (this.query.trim() === '') {
this.filtered.emit(this.items);
return;
}

const result = this.items.filter((item: any) => {
return item.toLowerCase().includes(this.query);
});

const noDataFound = ['No Data Found'];
if (result.length === 0) this.filtered.emit(noDataFound as any);
else this.filtered.emit(result);
}
}

Template

<input type="text" [appFilter]="items" (filtered)="afterFilter($event)" [filterKey]="searchText"
[(ngModel)]="searchText" placeholder="Search by name" />
<p> {{items | json}}</p>
<ul>
<li *ngFor="let item of filteredItems">
{{item}}
</li>
</ul>

Get Omdharani R J’s stories in your inbox

Join Medium for free to get updates from this writer.

Component

import { Component, computed, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { FilterDirective } from './filter';
import { FormsModule } from '@angular/forms';

@Component({
selector: 'app-root',
imports: [RouterOutlet, FilterDirective, FormsModule],
templateUrl: './app.html',
styleUrl: './app.css',
// changeDetection: ChangeDetectionStrategy.OnPush,
})
export class App {
items: any[] = [
'Omega',
'Alpha',
'Beta',
'Gamma',
'Om',
'BetaMax',
'Delta',
'Epsilon',
'Zeta',
'Eta',
'Theta',
];

filteredItems: any[] = this.items;
searchText: string = '';

afterFilter(filteredItems: any[]) {
console.log('Filtered items received in App component:', filteredItems);
this.filteredItems = filteredItems;
// this.items = filteredItems;
}

}

How it works

  1. Directive Input: The directive takes the list (items) and the search text (filterKey).
  2. Filtering Logic: On every change, it filters the list by checking if the item includes the search query.
  3. Event Emission: The filtered list is emitted back to the parent component.
  4. Fallback: If no matches are found, "No Data Found" is displayed.

--

--