10
votes

I've been trying to get this working with no luck. I've been referencing these resources for help: http://swimlane.github.io/ngx-datatable/#filter
https://github.com/swimlane/ngx-datatable/blob/master/demo/basic/filter.component.ts

Basically i just want to allow my filter to apply to more than a single column, without implementing code to handle every column. (Some datatables have 20+ columns!)

Example Code:

//HTML
  <input type='text' placeholder='Filter' (keyup)='updateFilter($event.target.value)' />

  <ngx-datatable
    class="material"
    columnMode="force"
    [columns]="gridProperties.FilteredColumns"
    [footerHeight]="50"
    [loadingIndicator]="gridLoadingIndicator"
    [rows]="filteredList"
    [scrollbarH]="false"
    [scrollbarV]="true"
    [selected]="selectedItem"
    [selectionType]="'single'"
    style="min-height:400px;">
  </ngx-datatable>

//TYPESCRIPT
  public items: Item[];

  updateFilter(filterValue) {
    const lowerValue = filterValue.toLowerCase();

    this.filteredList = this.items.filter(item => item.name.toLowerCase().indexOf(lowerValue) !== -1 || !lowerValue);
  }

Here I am obviously just handling filtering for the 'name' property of my items array. This works great as is, but like I had mentioned, if the grid contains many columns I'd like one method to handle all of them. Any help or tips are appreciated.

8
That would be a great feature :))Philip Enc
did you find a solution for this?mike_t
check this link I found this solution stackoverflow.com/questions/48299331/…ashokbalaga

8 Answers

13
votes

Using the example TS file for filtering (https://github.com/swimlane/ngx-datatable/blob/master/demo/basic/filter.component.ts) as a foundation, I was able to successfully make it filter all columns dynamically (it will filter all columns without the need to specify them). I've included what I believe to be all the necessary parts for this to work but also trimmed the code down as much as I could to make it easier to understand.

HTML

<ngx-datatable
 #table
 class="material striped scroll-vertical"
 [rows]="data"
 [columns]="cols"
 [columnMode]="'force'"
 [headerHeight]="35"
 [footerHeight]="35"
 [rowHeight]="'auto'"
 [limit]="pageSize"
 [selectionType]="'single'">

<input type="text" (keyup)='filterDatatable($event)'>

TYPESCRIPT

cols = [{name:'First Name'},{name:'Last Name'},{name:'Address'}];
data = [];
filteredData = [];

// dummy data for datatable rows
dummyData = [
  {firstName:'Daenarys',lastName:'Targaryen',address:'Dragonstone'},
  {firstName:'Sansa',lastName:'Stark',address:'Winterfell'},
  {firstName:'Cersei',lastName:'Lannister',address:'Kings Landing'},
  {firstName:'Brienne',lastName:'Tarth',address:'Sapphire Island'},
  {firstName:'Lyanna',lastName:'Mormont',address:'Bear Island'},
  {firstName:'Margaery',lastName:'Tyrell',address:'Highgarden'}
]

ngOnInit(){
  // populate datatable rows
  this.data = this.dummyData;
  // copy over dataset to empty object
  this.filteredData = this.dummyData;
}

// filters results
filterDatatable(event){
  // get the value of the key pressed and make it lowercase
  let val = event.target.value.toLowerCase();
  // get the amount of columns in the table
  let colsAmt = this.cols.length;
  // get the key names of each column in the dataset
  let keys = Object.keys(this.dummyData[0]);
  // assign filtered matches to the active datatable
  this.data = this.filteredData.filter(function(item){
    // iterate through each row's column data
    for (let i=0; i<colsAmt; i++){
      // check for a match
      if (item[keys[i]].toString().toLowerCase().indexOf(val) !== -1 || !val){
        // found match, return true to add to result set
        return true;
      }
    }
  });
  // whenever the filter changes, always go back to the first page
  this.table.offset = 0;
}
4
votes

This answer improves an existing answer by Cole Paciano :

  • Column names to be searched are only created once and not whenever a key is pressed
  • Cell with null values are correctly handled (no console errors)
  • Full rows are shown (because filter is applied to row array)
  • Column names to be searched can also be specified by hand to only include some of them (exclude guids, ids, etc)

Here are the steps:

In the template (html) file add an input with a keyup handler

Search:
<input type="text" (keyup)='filterDatatable($event)'>
<ngx-datatable
    class="material"
    [rows]="rows"
    [columns]="columns"
    headerHeight="35"
    rowHeight ="35">
</ngx-datatable>

In the component add the following filteredData and columnsWithSearch

export class ListParkingsComponent implements OnInit {
  columns = [];
  rows = [];
  filteredData = [];
  columnsWithSearch : string[] = [];

ngOnInit() {
    this.rows = getData() ; //recover data from API/database/datasource
    this.filteredData = this.rows;
    // for specific columns to be search instead of all you can list them by name
    this.columnsWithSearch = Object.keys(this.rows[0]);
}

getData() {
   //your current logic to fill the rows of the table
}

// filters results
filterDatatable(event){
    // get the value of the key pressed and make it lowercase
    let filter = event.target.value.toLowerCase();

    // assign filtered matches to the active datatable
    this.rows = this.filteredData.filter(item => {
      // iterate through each row's column data
      for (let i = 0; i < this.columnsWithSearch.length; i++){
        var colValue = item[this.columnsWithSearch[i]] ;

        // if no filter OR colvalue is NOT null AND contains the given filter
        if (!filter || (!!colValue && colValue.toString().toLowerCase().indexOf(filter) !== -1)) {
          // found match, return true to add to result set
          return true;
        }
      }
    });
    // TODO - whenever the filter changes, always go back to the first page
    //this.table.offset = 0;
}
1
votes

here is an example of your code with multiple columns filtering:

updateFilter(filter: string): void {

  const val = filter.trim().toLowerCase();

  this.filteredList = this.items.slice().filter((item: any) => {
    let searchStr = '';
    for (let i = 0; i < this.gridProperties.FilteredColumns.length; i++) {
      searchStr += (item[this.gridProperties.FilteredColumns[i]]).toString().toLowerCase();
    }
    return searchStr.indexOf(val) !== -1 || !val;
  });
}

If I did not made any errors, it should work correctly.

1
votes

While storing the data in rows list at that time also initilize perttemp list, so that we can fetch after filter

updateFilter(event) {

    const val = event.target.value.toLowerCase();
    if(val) {
        this.temp = this.rows;
        // filter our data
        const temp = this.temp.filter(function (d) {
          return ( d.name.toLowerCase().indexOf(val) !== -1 || d.email.toLowerCase().indexOf(val) !== -1 || !val);
        });
        this.rows = temp;
    }
    else
    {
        this.rows = this.perttemp;
    }
}
0
votes
 updateFilter(event) {
    const val = event.target.value.toLowerCase();
    const temp = this.temp.filter(index => {
      return (index.name.toLowerCase().indexOf(val) !== -1 ||
        index.company.toLowerCase().indexOf(val) !== -1 ||
        index.gender.toLowerCase().indexOf(val) !== -1 ||
        !val);
    });
    this.company = temp;
    this.table.offset = 0;
  }
0
votes
import { DatatableComponent } from '@swimlane/ngx-datatable';
ViewChild(DatatableComponent) table: DatatableComponent;

   updateFilter(event) {
    const val = event.target.value.toLowerCase();
    var returnData: any;
    // filter our data
    const temp = this.temp.filter(function (d) {
      if (d.yourFirstColumnName.toLowerCase().indexOf(val) !== -1 || !val) {
        returnData = d.user_name.toLowerCase().indexOf(val) !== -1 || !val;
      } else if (d.yourSecondColumnName.toLowerCase().indexOf(val) !== -1 || !val) {
        returnData = d.notes_title.toLowerCase().indexOf(val) !== -1 || !val;

      }
      return returnData;
    });
 <input placeholder="Search Order" (keyup)='updateFilter($event)'>
0
votes

You might be helped

 <input
        type="text"
        style="padding:8px;margin:15px auto;width:30%;"
        placeholder="Type to filter the name column..."
        (keyup)="updateFilter($event)"
      />
      <ngx-datatable
        #table
        class="material"
        [columns]="columns"
        [columnMode]="ColumnMode.force"
        [headerHeight]="50"
        [footerHeight]="50"
        rowHeight="auto"
        [limit]="10"
        [rows]="rows"
      >
      </ngx-datatable>




updateFilter(event) {
    const val = event.target.value.toLowerCase();

    // filter our data
    const temp = this.temp.filter(function(d) {
      return d.name.toLowerCase().indexOf(val) !== -1 ||
        d.address.toLowerCase().indexOf(val) !== -1 ||
        d.gender.toLowerCase().indexOf(val) !== -1 || !val;
    });

    // update the rows
    this.rows = temp;
    // Whenever the filter changes, always go back to the first page
    this.table.offset = 0;
  }

https://github.com/swimlane/ngx-datatable/blob/master/src/app/basic/dark-theme.component.ts

0
votes

There were a few issues when using some of the above answers to i am adding my own...

First its best to store the keys in a global variable, because things get overwritten on each iteration.

problems solved...

  1. if data is pushed via an input we need to reset the keys especially if there was no data to begin with.

  2. Store everything in a temp variable to always retain original data.**

I am posting my entire usage here..

import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'verasci-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.scss'],
})
export class DataTableComponent implements OnInit, OnChanges {
  @ViewChild('table') table;
  @Input() rows;
  @Input() columns;
  @Input() searchBar;
  keys;
  temp: Array<any>;
  @Output() selectEvent$ = new EventEmitter();
  selected = [];
  readonly pageLimit = 10;
  readonly headerHeight = 50;
  readonly rowHeight = 50;

  constructor(private readonly el: ElementRef, private readonly router: Router) { }

  async ngOnInit() {
    this.temp = this.rows;
    this.keys = this.rows[0] ? Object.keys(this.rows[0]) : null;
    setTimeout(() => { this.dataLoaded = true; }, 200);
  }

  // we need to reset keys and temp data if data is pushed to the table.
  ngOnChanges(changes: SimpleChanges) {
    this.keys = this.rows[0] ? Object.keys(this.rows[0]) : null;
    this.temp = this.rows;
  }       

  filterSingle(event) {
    const val = event.target.value.toLowerCase();

    // filter our data
    const temp = this.temp.filter((d) => {
      return d.sponsor.toLowerCase().indexOf(val) !== -1 || !val;
    });

    // update the rows
    this.rows = temp;
    // Whenever the filter changes, always go back to the first page
    this.table.offset = 0;
  }

  filterMulti(event) {
    // get the value of the key pressed and make it lowercase
    const val = event.target.value.toLowerCase();
    // get the amount of columns in the table
    const colsAmt = this.columns.length;
    // get the key names of each column in the dataset
    const keys = this.keys; // just need to keys from the first set of rw
    // filter our data
    const temp = this.temp.filter(item => {
    // iterate through each row's column data
    for (let i = 0; i < colsAmt; i++) {
    // check for a match on properties that are not null
      if (item[keys[i]] != null && (item[keys[i]].toString().toLowerCase().indexOf(val) !== -1 || !val)) {
        // found match, return true to add to result set
        return true;
      }
     }
   });
    // update the rows
    this.rows = temp;
    // Whenever the filter changes, always go back to the first page
    this.table.offset = 0;
  }

  onSelect({ selected }) {
    this.selectEvent$.emit({ selected: selected[0] });
  }

}