4

I Want to filter the items in an ngFor loop in Angular 4 based on the fact if a particular substring exist inside a string

For Ex:

agents[] = [
  { id: 11, name: 'Agent 0', email:'admin@ab.com' },
  { id: 12, name: 'Agent 1', email:'admin@ab.com'},
  { id: 13, name: 'Agent 2', email:'admin@ab.com' },
  { id: 14, name: 'Agent 3', email:'admin@ab.com' },
  { id: 15, name: 'Agent 4', email:'admin@ab.com' },
  { id: 16, name: 'Agent 5', email:'admin@ab.com' },
  { id: 17, name: 'Agent 6', email:'admin@ab.com' },
  { id: 18, name: 'Agent 7', email:'admin@ab.com' },
  { id: 19, name: 'Agent 8', email:'admin@ab.com' },
  { id: 20, name: 'Agent 9', email:'admin@ab.com' }
];

Search in Name

Show only Agent 0 when search for 0 show only agent 1 when search for 1 show all when search for 'a', 'g', 'e', 'n','t', ' ' etc;

i have managed to save a input from the text box in {{ab}}

i.e.

agent.component.html

<input (keyup)="onagentKey($event)"  class="form-control input-lg" type="text" placeholder="Find a Agent....">
<div *ngFor = "let agent of agents" class="col-sm-4">Name : {{agent.name}}<br>
                        Email : {{agent.email}}
                        </div>

agent.component.ts

values = '';
ab = '';
 onagentKey(event: any) { 
     this.values = event.target.value;
   this.ab = this.values;

  }

How does one use a filter, pipe or any other operation to solve this problem?

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Rishabh Agrawal
  • 101
  • 1
  • 1
  • 8
  • 3
    Possible duplicate of [How to apply filters to \*ngFor](https://stackoverflow.com/questions/34164413/how-to-apply-filters-to-ngfor) – Veera Jul 03 '17 at 14:48

2 Answers2

10

My faovrite solution is to use a pipe :

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

@Pipe({
  name: 'fullTextSearch',
  pure: false
})
export class FullTextSearchPipe implements PipeTransform {

  constructor() { }

  transform(value: any, query: string, field: string): any {
      return query ? value.reduce((prev, next) => {
        if (next[field].includes(query)) { prev.push(next); }
        return prev;
      }, []) : value;
    }
}

And in your HTML

<input type="text" [(ngModel)]="query">
<div *ngFor="let agent of agents | fullTextSearch:query:'name'"><!-- ... --></div>

If you need help to read the code, feel free to ask

  • 1
    Thankx a lot but it is showing an error when trying to run it ERROR in C:/Users/dgdg/Desktop/Angular4/src/app/brokers/agents.pipe.ts (18,3): 'finally' expected. webpack: Failed to compile. – Rishabh Agrawal Jul 03 '17 at 14:52
  • add finally or catch block after try block – Sreemat Jul 03 '17 at 14:55
  • Sorry I made a copy paste mistake, just get rid of the try just under transform ! –  Jul 03 '17 at 14:55
  • 1
    Thankx a lot man, it works perfectly. If Could jsut help me with one more thing, i want the search to be case insensitive. – Rishabh Agrawal Jul 03 '17 at 15:11
  • Sure, replace `next[field].includes(query)` with `next[field].toUpperCase().includes(query.toUpperCase())` –  Jul 03 '17 at 15:14
  • 1
    Well.. I have to say that *impure pipes* are, in majority, really a bad choice. The most performatic way to this is to call a method when `query` *variable* changes (`(change)="aMethod($event)"`) and assign a variable to the result: `this.filteredItems = ` and in template `*ngFor="let agent of filteredItems"`. For sure, it'll increase the performance. Also, note that `.includes` isn't supported in some browsers (versions). – developer033 Jul 03 '17 at 15:25
  • I used an impure pipe for my project because of reasons. But in no way you are forced to do the same, again it's a mistake I made from copy pasting, sorry about that. –  Jul 03 '17 at 15:27
  • How can I put multiple fields? – Inderjeet Jul 10 '18 at 09:32
  • Take a look at [this pipe on another question](https://stackoverflow.com/questions/51260154/datatable-filter-logic-change-not-reflecting/51260432?noredirect=1#comment89500444_51260432), that checks every field in your object –  Jul 10 '18 at 09:34
0

My solution is almost identical to @Maryannah, but in case if anyone wants to search among entire object values, rather than a single field, it might be valuable.

@Pipe({
    name: 'textSearch',
    pure: false
})
export class TextSearchPipe implements PipeTransform {
    transform(value: any[], query: string): any[] {
        return query
            ? value.filter(
                  obj =>
                      Object.keys(obj)
                          .map(key => obj[key])
                          .toString()
                          .toLocaleLowerCase()
                          .indexOf(query.toLocaleLowerCase()) !== -1
              )
            : value;
    }
}
Timothy
  • 3,213
  • 2
  • 21
  • 34