Reputation: 10470
I wanted to add a custom table filter to my angular code. Did web search and found this blog post:
https://www.code-sample.com/2018/07/angular-6-search-filter-pipe-table-by.html
It works pretty well and here the pipe code:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'gridFilter'
})
/*
I got this code from here:
https://www.code-sample.com/2018/07/angular-6-search-filter-pipe-table-by.html
*/
export class GridFilterPipe implements PipeTransform {
transform(items: any, filter: any, defaultFilter: boolean): any {
if (!filter){
return items;
}
if (!Array.isArray(items)){
return items;
}
if (filter && Array.isArray(items)) {
let filterKeys = Object.keys(filter);
if (defaultFilter) {
return items.filter(item =>
filterKeys.reduce((x, keyName) =>
(x && new RegExp(filter[keyName], 'gi').test(item[keyName])) || filter[keyName] == "", true));
}
else {
return items.filter(item => {
return filterKeys.some((keyName) => {
return new RegExp(filter[keyName], 'gi').test(item[keyName]) || filter[keyName] == "";
});
});
}
}
}
}
It works pretty well for most of my needs except this one table that is this html:
<div class="col-md-4" *ngIf="element == 'location'">
<div class="spacer"></div>
<div class="panel panel--vibblue panel--raised">{{ element | titlecase }}</div>
<div class="responsive-table panel--raised">
<table class="table table--bordered table--hover approvals-table" id="location-table">
<thead>
<tr>
<th class="sortable">{{ element | titlecase }} Name <span class="sort-indicator icon-chevron-down"></span></th>
<th class="sortable">Site <span class="sort-indicator icon-chevron-down"></span></th>
<th>Action</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let el of elements | gridFilter: {name: searchText, site:searchText}">
<tr>
<td>{{el.name}}</td>
<td>{{ getSiteName(el.site) }}</td>
<td>
<a><span class="icon-trash" (click)="deleteElement(el.id, el.name)"></span></a>
</td><td>
<a><span class="icon-pencil" (click)="editElement(el)"></span></a>
</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
</div>
The issue with the above table is this. To display the site name of a location I use this html ...
<td>{{ getSiteName(el.site) }}</td>
... which calls a method getSiteName
which takes the id
of a site and returns the site's name. I do not know how to set my gridFilter
. So if I try to search for a site name my gridFilter doesn't find the site.
Here is my stackblitz for this: https://stackblitz.com/edit/angular-damcul
** UPDATE **
I have updated my stackblitz to actually illustrate the problem I am having. Here is a screen shot of the stackblitz app:
I can search for locations and here is the screen shot of me searching for 'loc_2':
But I can not search for sites. Here the screen shot of me searching for any 'site'.
Upvotes: 3
Views: 3802
Reputation: 15031
made some changes:
grdFilter
, in your stackblitz it was 'gridFilter'getSiteName
was added, which takes in the object and returns the blog name (there is no property named 'site' in our data)<ng-container *ngFor="let el of customerData | gridFilter: {name: searchText, site:searchText}">
<ng-container *ngFor="let el of customerData | grdFilter: {name: searchText, blog:searchText}">
relevant hello.componenet.ts:
import { Component, Input } from '@angular/core';
@Component({
selector: 'hello',
template: `<input [(ngModel)]="searchText" placeholder="Search.." class="advancedSearchTextbox">
<p></p>
<table *ngFor="let emp of customerData | grdFilter: {name: searchText, Age:searchText, blog: searchText}; let i=index;">
<tr>
<td style="width: 5%;">{{i +1}}</td>
<td style="width: 10%;">{{emp.name}}</td>
<td style="width: 5%;">{{emp.Age}}</td>
<td style="width: 15%;">{{emp.blog}}</td>
</tr>
</table>
<hr/>
<table class="table table--bordered table--hover approvals-table" id="location-table">
<thead>
<tr>
<th class="sortable">{{ element | titlecase }} Name <span class="sort-indicator icon-chevron-down"></span></th>
<th class="sortable">Site <span class="sort-indicator icon-chevron-down"></span></th>
<th>Action</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let el of customerData | grdFilter: {name: searchText, blog:searchText}">
<tr>
<td>{{el.name}}</td>
<td>{{ getSiteName(el) }}</td>
<td>
<a><span class="icon-trash" (click)="deleteElement(el.id, el.name)"></span></a>
</td><td>
<a><span class="icon-pencil" (click)="editElement(el)"></span></a>
</td>
</tr>
</ng-container>
</tbody>
</table>
`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
public searchText: string;
public customerData: any;
@Input() name: string;
constructor() { }
ngOnInit() {
this.customerData = [
{ "name": 'Anil kumar', "Age": 34, "blog": 'https://code-view.com' },
{ "name": 'Sunil Kumar Singh', "Age": 28, "blog": 'https://code-sample.xyz' },
{ "name": 'Sushil Singh', "Age": 24, "blog": 'https://code-sample.com' },
{ "name": 'Aradhya Singh', "Age": 5, "blog": 'https://code-sample.com' },
{ "name": 'Reena Singh', "Age": 28, "blog": 'https://code-sample.com' },
{ "name": 'Alok Singh', "Age": 35, "blog": 'https://code-sample.com' },
{ "name": 'Dilip Singh', "Age": 34, "blog": 'https://code-sample.com' }];
}
getSiteName(passedObj) {
return passedObj.blog;
}
}
update #1 in light of questioner's comment & updated question:
if you go to the grd-pipe.ts file, and do console.log(items,filter)
, you'll see that the array that we're working on inside the custom pipe is the locations array, which has the 'sites' column values as 1, 2 or 3. The site name which you have fetched for the UI from sites array is not part of the locations array and hence can't be processed by the pipe.
So, we create a new field siteName in the locations array where we store the site name (from sites array), now since the field is available inside the pipe, the pipe can search on it also.
relevant changes in TS
ngOnInit(){
for(var i=0;i<this.locations.length;i++){
this.locations[i].siteName = this.sites.find(s => s.id === this.locations[i].site).name;
}
}
relevant changes in HTML the added field siteName added to the filter:
<table class="table table--bordered table--hover approvals-table" id="location-table">
<tbody>
<ng-container *ngFor="let loc of locations | grdFilter: {name: searchText, siteName:searchText}; let i=index">
<tr>
<td style="width: 5%;">{{i +1}}</td>
<td style="width: 10%;">{{loc.name}}</td>
<td style="width: 5%;">{{getSiteName(loc.site)}}</td>
</tr>
</ng-container>
</tbody>
</table>
working stackblitz here is also updated
Upvotes: 3