43

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?

11 Answers 11

48

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 :)

Sign up to request clarification or add additional context in comments.

12 Comments

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.
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 anymore
Glad to hear this piece of code still helps ;) Link to the new editor: stackblitz.com/edit/angular-dukfxf
@GoranSu 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 ?
I used (closed) event of mat select to clear the search string and re-assign original list to the list being displayed
|
14

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

In your component.html

In your compponent.ts

1 Comment

Looks like the better solution when using the material way. There is also an example Require an autocomplete option to be selected where you can enforce to use one of the provided options.
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

Comments

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] );
            }
        }

2 Comments

I think this is as simple as saying: this.dataArray = this.meta.data.filter( i => i.name.toLowerCase().indexOf(filter) >= 0 );
Very interesting solution ! I didn't find any other doc talking about putting an input tag inside a select, but this works pretty smoothly
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.

Comments

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

Comments

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()
    }
}

Comments

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

1 Comment

Error in stackblitz link -> cannot get api angular v 1
0

We Can do this

1. Saving Multi-Select Values

To bind the selected programme objects correctly, update the mat-option as follows:

<mat-select class="form-control" placeholder="Funding Programmes" formControlName="Programme" multiple>
  <mat-option *ngFor="let programme of programmeList" [value]="programme">
    {{programme.Description}}
  </mat-option>
</mat-select>

This ensures the selected Programme objects are stored as an array in the form control.

2. Dropdown Behind Modal

To fix the issue of the dropdown appearing behind the modal, add this CSS:

::ng-deep .mat-select-panel {
  z-index: 1050 !important;
}

This ensures the dropdown is displayed above the modal.

Key Points:

  • Bind each mat-option to individual programme objects to save them correctly.
  • Use ::ng-deep to adjust the dropdown's z-index so it appears on top of the modal.

Hope this solves your Question

Comments

-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;

`

Comments

-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>

1 Comment

Please describe what was wrong and what exactly do your solution

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.