Implement a search filter for the <mat-select> component of angular material
Asked Answered
C

10

42

Trying to implement a simple application in angular 2 using angular material.
I implemented a simple table with pagination .

I also used mat-select component, but for this i want implement a search filter to type and search the required option from the list.

Below shown is my .html file

<table>
 <tr><td> Department</td>
<td>
  <mat-form-field>
  <mat-select placeholder=" ">
    <mat-option> </mat-option>
    <mat-option *ngFor="let dep of dept" [value]="dep">{{dep}}</mat-option>
  </mat-select>
</mat-form-field><br/>
</td>
</tr>


</table>

<br><br>

<button >Search</button>

<button >Reset</button>

<button >Close</button>


<mat-card>
<div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource">

    <!-- Account No. Column -->
    <ng-container matColumnDef="accno">
      <mat-header-cell *matHeaderCellDef> Account No. </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.accno}} </mat-cell>
    </ng-container>

    <!-- Account Description Column -->
    <ng-container matColumnDef="accdesc">
      <mat-header-cell *matHeaderCellDef> Account Description </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.accdesc}} </mat-cell>
    </ng-container>

    <!-- Investigator Column -->
    <ng-container matColumnDef="investigator">
      <mat-header-cell *matHeaderCellDef> Investigator </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.investigator}} </mat-cell>
    </ng-container>

    <!-- Account CPC Column -->
    <ng-container matColumnDef="accCPC">
      <mat-header-cell *matHeaderCellDef> Account CPC </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.accCPC}} </mat-cell>
    </ng-container>

     <!-- Location Column -->
    <ng-container matColumnDef="location">
      <mat-header-cell *matHeaderCellDef> Location </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.location}} </mat-cell>
       </ng-container>


 <!-- Client Dept ID Column -->
    <ng-container matColumnDef="cdeptid">
      <mat-header-cell *matHeaderCellDef> ClientDeptID </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.cdeptid}} </mat-cell>
       </ng-container>


        <!-- Dept Description Column -->
    <ng-container matColumnDef="depdesc">
      <mat-header-cell *matHeaderCellDef> Dept Description  </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.depdesc}} </mat-cell>
       </ng-container>


    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>

  <mat-paginator #paginator
                 [pageSize]="10"
                 [pageSizeOptions]="[5, 10, 20]">
  </mat-paginator>
</div>
</mat-card>

Below shown is my .ts file

import {Component, ViewChild} from '@angular/core';
import {MatPaginator, MatTableDataSource} from '@angular/material';

@Component({
  selector: 'app-account',
  templateUrl: './account.component.html',
  styleUrls: ['./account.component.scss']
})

export class AccountComponent {



  dept = [
    'Administrative Computer',
    'Agosta Laboratory',
    'Allis Laboratory',
    'Bargaman Laboratory',
    'Bio-Imaging Resource Center',
    'Capital Projects',
    'Casanova Laboratory',
    'Darst Laboratory',
    'Darnell James Laboratory',
    'Deans Office',
    'Energy Consultant',
    'Electronic Shop',
    'Facilities Management',
    'Field Laboratory'
  ];


  displayedColumns = ['accno', 'accdesc', 'investigator', 'accCPC','location','cdeptid','depdesc'];
  dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);

  @ViewChild(MatPaginator) paginator: MatPaginator;

   ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
  }
}

export interface Element {
  accno: number;
  accdesc: string;
  investigator: string;
  accCPC: string;
  location:string;
  cdeptid: number;
  depdesc: string;
}

const ELEMENT_DATA: Element[] = [
  {accno: 5400343, accdesc: 'ASTRALIS LTD', investigator:'Kruger, James G.', accCPC: 'OR',location:'ON',cdeptid: 110350,depdesc: 'Kruger Laboratory'}

  ];

can anybody please help me to implement search filter with mat-select component in my application?

Contentious answered 25/1, 2018 at 12:22 Comment(0)
C
0

Just and this code in the search event method:

keyUp(data:any){
    this.dataSource.filter= data.toLowerCase()

    if(this.dataSource.paginator){
        this.dataSource.paginator.firstPage()
    }
}
Cyanotype answered 24/8, 2023 at 11:55 Comment(0)
P
45

HTML

<h4>mat-select</h4>
<mat-form-field>
  <mat-label>State</mat-label>
  <mat-select>
     <input (keyup)="onKey($event.target.value)"> // **Send user input to TS**
    <mat-option>None</mat-option>
    <mat-option *ngFor="let state of selectedStates" [value]="state">{{state}}</mat-option>
  </mat-select>
</mat-form-field>

TS

states: string[] = [
    'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware',
    'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky',
    'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi',
    'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico',
    'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania',
    'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont',
    'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'
  ];

**// Initially fill the selectedStates so it can be used in the for loop** 
selectedStates = this.states; 

**// Receive user input and send to search method**
onKey(value) { 
this.selectedStates = this.search(value);
}

**// Filter the states list and send back to populate the selectedStates**
search(value: string) { 
  let filter = value.toLowerCase();
  return this.states.filter(option => option.toLowerCase().startsWith(filter));
}

The solution is rather easy. Should work like a charm :)

Plasterboard answered 1/7, 2019 at 8:57 Comment(12)
this will not filter the dropdown, it will only tell true or false.Mairemaise
the problem is you have copied and pasted from stackblitz but you didnt know what is happening here.Mairemaise
Not sure what you are talking about, I used the code above in my application. I created the stackblitz with the same code to prove it works.Plasterboard
Worked for me and saved me hours! On a side note, use option.toLowerCase().includes(filter) if you wish to search for the text anywhere in the strings. @GoranSu, the Stackblitz link doesn't work anymoreClytemnestra
Glad to hear this piece of code still helps ;) Link to the new editor: stackblitz.com/edit/angular-dukfxfPlasterboard
If you click the spacebar (e.g. because you are trying to search for New Hampshire and you type in new ) it closes the dropdown.Breastbeating
You can try adding <mat-select (keydown)="$event.stopPropagation()"> To read more about it, visit: #5964169Plasterboard
@Plasterboard how can I auto-focus the search field? Your solution works fine but it doesn't autofocus my search field.Templet
@Avishek check out this thread: #41874393Plasterboard
I passed the complete event to the component as (keyup)="onKey($event)" and function signature onKey($event: Event) then fetched the value inside the function as const filterValue = ($event.target as HTMLInputElement).value;Kuhl
@Plasterboard Thanks for the solution. I am facing one issue - If I type a search string which does not match any option, the dropdown list becomes empty. If I click outside the dropdown and tries to open the dropdown again it does not open again as the search string is not cleared. How to fix this ?Seating
I used (closed) event of mat select to clear the search string and re-assign original list to the list being displayedSeating
S
13

Btw. This might be supported in some of upcoming the Angular Material releases. Discussion is already open: https://github.com/angular/components/issues/5697

Sleep answered 23/6, 2020 at 15:45 Comment(0)
W
12

You can use the Angular Material Autocomplete: Angular Material Autocomplete Component

In your component.html

In your compponent.ts

Waft answered 18/10, 2021 at 4:4 Comment(1)
Thanks for sharing screenshot. It is very useful.Putdown
M
5

I found the solution for searching the dropdown values

Here is the html code

<mat-select class="yourClass" [(ngModel)]="searchObj">
            <input class="yourClass" placeholder ="search " (keyup)="onKey($event.target.value)"> 
            <mat-option *ngIf="someCondition" [value]='Select'>Select</mat-option>
            <mat-option *ngFor="let data of dataArray" [value]="data">{{data}}</mat-option>
          </mat-select>

Here is the typescript code key function getting the entered value

    onKey(value) { 
            this.dataArray= []; 
            this.selectSearch(value);       
        }

performing the searching given value inside the provided values in dropdown

selectSearch(value:string){
            let filter = value.toLowerCase();
            for ( let i = 0 ; i < this.meta.data.length; i ++ ) {
                let option = this.meta.data[i];
                if (  option.name.toLowerCase().indexOf(filter) >= 0) {
                    this.dataArray.push( option );
                }
            }
        }

this function is goes where your main search function calling api and getting the data

this.meta.data

        searchDpDropdown(){
            for ( let i = 0 ; i < this.meta.data.length; i ++ ) {
                this.dataArray.push( this.meta.data[i] );
            }
        }
Mairemaise answered 9/8, 2019 at 7:7 Comment(2)
I think this is as simple as saying: this.dataArray = this.meta.data.filter( i => i.name.toLowerCase().indexOf(filter) >= 0 );Aiguillette
Very interesting solution ! I didn't find any other doc talking about putting an input tag inside a select, but this works pretty smoothlyThomism
C
3

I solved this using Reactive Form.

  • Whenever I am getting the data I am storing that in two arrays.
    1. One Array can be use to filter out the data
    2. Second Array can be use to display data in our case dept.
  • Added ValueChanges listener to filter the data using the search text.

HTML

<mat-select placeholder="">
<input class="search-box" (keydown)="$event.stopPropagation()" formControlName="searchTxt" placeholder="Search" matInput>    
    <mat-option *ngFor="let dep of dept" [value]="dep">{{dep}}</mat-option>
  </mat-select>
</mat-form-field>

TS

 this.FormName.controls['searchTxt'].valueChanges.subscribe((data: any)=> {
  this.dept= this.backupDept.filter(option => option.toLowerCase().trim().includes(data.toLowerCase()));
})

You can also use this on Objects just add the proper filter name in valueChanges listener.

Console answered 26/9, 2022 at 12:25 Comment(0)
P
1

I've found out that primefaces for angular does have a multiselect list that allows you to search for listitems. It also includes a builtin select-all button! You can find it here https://www.primefaces.org/primeng/#/multiselect You can install primefaces with npm install primeng --save

Parshall answered 21/5, 2018 at 18:54 Comment(0)
M
0

Instead of customizing the MatSelect in order to add typing search functionality to a list of options, it's optimal to use MatAutocomplete

The autocomplete is a normal text input enhanced by a panel of suggested options.

HTML:

<form class="example-form">
    <input type="text" placeholder="Search for a street" [formControl]="control" 
 [matAutocomplete]="auto">
         <mat-autocomplete #auto="matAutocomplete">
              <mat-option *ngFor="let street of filteredStreets | async" [value]="street">
      {{street}}
             </mat-option>
         </mat-autocomplete>
</form>

TS

control = new FormControl();
  streets: string[] = ['Champs-Élysées', 'Lombard Street', 'Abbey Road', 'Fifth Avenue'];
  filteredStreets: Observable<string[]>;

  ngOnInit() {
    this.filteredStreets = this.control.valueChanges.pipe(
      startWith(''),
      map(value => this._filter(value))
    );
  }

  private _filter(value: string): string[] {
    const filterValue = this._normalizeValue(value);
    return this.streets.filter(street => this._normalizeValue(street).includes(filterValue));
  }

  private _normalizeValue(value: string): string {
    return value.toLowerCase().replace(/\s/g, '');
  }

Refferences: Demo (StackBlitz) || Official Docs & Examples

Monotype answered 9/6, 2020 at 11:45 Comment(1)
Error in stackblitz link -> cannot get api angular v 1Clockwise
C
0

Just and this code in the search event method:

keyUp(data:any){
    this.dataSource.filter= data.toLowerCase()

    if(this.dataSource.paginator){
        this.dataSource.paginator.firstPage()
    }
}
Cyanotype answered 24/8, 2023 at 11:55 Comment(0)
B
-1

I have done some work around for this even though its not correct implementation

component.hml

`

<mat-select formControlName="buyersCountryCode" matInput placeholder="Buyer's Country" required>
                    <input #buyersCountryQuery matInput placeholder="Search"  class="search-input" (keyup)="filterDropDowns(buyersCountryQuery.value, 'BUYERSCOUNTRY')"> 
                    <mat-option *ngFor="let country of filteredBuyersCountry" [value]="country.buyersCountryCode">{{country.buyersCountryValue}}</mat-option>
                </mat-select>

`

component.ts

`

this.filteredBuyersCountry = query
          ? this.filteredBuyersCountry.filter(item =>
              item.buyersCountryValue
                .toLocaleLowerCase()
                .includes(query.toLowerCase())
            )
          : this.dropDowns.buyersCountry;

`

Bedford answered 21/11, 2018 at 4:44 Comment(0)
E
-1
> <mat-form-field>
<mat-label>Items</mat-label>
<mat-select name="item" [(ngModel)]="selected">
  <form>
    <mat-form-field class="w-100">
      <input name="query" type="text" [(ngModel)]="query" matInput>
      <mat-icon matSuffix>search</mat-icon>
    </mat-form-field>
  </form>
  <mat-option>
  </mat-option>
  <mat-option *ngFor="let option of options|appFilter:query" [value]="option">
    {{option.label}}
  </mat-option>
</mat-select>

Escadrille answered 6/2, 2020 at 12:59 Comment(1)
Please describe what was wrong and what exactly do your solutionSarsenet

© 2022 - 2024 — McMap. All rights reserved.