Angular 5 Material mat select: filter based on value selected,

Tempuslight picture Tempuslight · Jun 28, 2018 · Viewed 7k times · Source

So I have an Angular 5 app using Angular Material Data Table and on it I have filtering, sorting, pagination etc. Right now I can filter based on a value a user inputs in an input field, this works. Now I want to have the same based on a select input. So there's 2 inputs a user can user: 1 input field where user can type text, 1 select field where user can select one of the options. I want my table to also filter based on what option the user has selected, but right now it doesn't work.

Here's my HTML:

<div class="filterContainer" *ngIf="showDataForm">
  <mat-form-field>
    <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Search">
  </mat-form-field>
</div>

<div class="filterSelectContainer" *ngIf="showDataForm">
  <mat-select placeholder="Search EADDPStage">
    <mat-option *ngFor="let stage of EADDPStages[0]" [value]="stage" (onSelectionChange)="applySelectFilter($event, stage)">
      {{stage}}
    </mat-option>
  </mat-select>
</div>

<div class="container">
  <mat-table #table class="dataTable" *ngIf="showDataForm;else loadingTemplate" [dataSource]="dataSource" matSort>
    <ng-container matColumnDef="EOTAFileNr">
      <mat-header-cell *matHeaderCellDef mat-sort-header>EOTAFileNr</mat-header-cell>
      <mat-cell *matCellDef="let item">{{item.EOTAFileNr}}</mat-cell>
    </ng-container>
    <ng-container matColumnDef="ProcessRTAB">
      <mat-header-cell *matHeaderCellDef mat-sort-header>ProcessRTAB</mat-header-cell>
      <mat-cell *matCellDef="let item">{{item.ProcessRTAB}}</mat-cell>
    </ng-container>
    <ng-container matColumnDef="EADDPStage">
      <mat-header-cell *matHeaderCellDef mat-sort-header>EADDPStage</mat-header-cell>
      <mat-cell *matCellDef="let item">{{item.EADDPStage}}</mat-cell>
    </ng-container>
    <ng-container matColumnDef="RegistrationDate">
      <mat-header-cell *matHeaderCellDef mat-sort-header>RegistrationDate</mat-header-cell>
      <mat-cell *matCellDef="let item">{{item.RegistrationDate}}</mat-cell>
    </ng-container>

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

  <mat-paginator *ngIf="showDataForm" [pageSize]="10" [pageSizeOptions]="[5, 10, 25]" showFirstLastButtons></mat-paginator>
</div>

<ng-template #loadingTemplate>
  <div>
      <p>Please wait, the data is loading...</p>
      <img src="../../assets/giphy.gif">
  </div>
</ng-template>

<button mat-raised-button class="submitButton" color="accent" (click)="logout()">Logout and remove cookie</button>

Here's my TS file:

import { Component, OnInit, ChangeDetectorRef, ViewChild, ElementRef } from '@angular/core';
import { Router } from '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { LoginService } from '../Services/login.service';
import { TableService } from '../Services/table.service';
import { EADProcess } from '../Classes/EADProcess';
import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map, tap, catchError } from 'rxjs/operators';

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.css']
})
export class TableComponent implements OnInit {

  sort;
  paginator;

  showDataForm = false;

  stringArray: string[] = [];
  eadItems: EADProcess[] = [];
  EADDPStages: string[] = [];

  dataSource: MatTableDataSource<EADProcess>;

  // @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatPaginator) set contentPaginator(contentPaginator: ElementRef) {
    this.paginator = contentPaginator;
    if (this.paginator) {
      this.dataSource.paginator = this.paginator;
    }
  }
  @ViewChild(MatSort) set contentSort(contentSort: ElementRef) {
    this.sort = contentSort;
    if (this.sort) {
      this.dataSource.sort = this.sort;
    }
  }

  // which columns the data table needs to display
  columnsToDisplay: string[] = ['EOTAFileNr', 'ProcessRTAB', 'EADDPStage', 'RegistrationDate'];

  constructor(private router: Router,
              private cookieService: CookieService,
              private loginService: LoginService,
              private tableService: TableService,
              private chRef: ChangeDetectorRef) {

  }

  ngOnInit() {
    const $this = this;

    this.getEADDPStages();
  }

  public getEADDPStages() {
    const json: any = {(data omitted for example)};

    const jsonStringified = JSON.stringify(json);

    this.tableService.getEADDPStages(jsonStringified).subscribe(res => {
      this.convertJsonEADDPStagesToArray(res);
      this.getAllEadItems();
    });
  }

  public getAllEadItems() {
    const json: any = {(data omitted for example)};

    const jsonStringified = JSON.stringify(json);

    this.tableService.getAllEadItems(jsonStringified).subscribe(res => {
      this.convertJsonResultToArray(res);
      this.dataSource = new MatTableDataSource(this.eadItems);
      this.dataSource.paginator = this.paginator;
      this.showDataForm = true;
    });
  }

  public applyFilter(filterValue: string) {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

  public applySelectFilter(event: Event, filterValue: string) {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

  public convertJsonResultToArray(res: any) {
    this.stringArray = JSON.parse(res);
    for (const eadItem of this.stringArray) {
      const ead = new EADProcess();
      ead.id = eadItem['GUID'];
      ead.titel = eadItem['Title'];
      ead.EADDraftingStage = eadItem['EADDraftingStage'];
      ead.EOTAFileNr = eadItem['EOTAFileNr'];
      ead.ProcessRTAB = eadItem['ProcessRTAB']['LookupValue'];
      ead.EADDPStage = eadItem['EADDPStage'];
      ead.RegistrationDate = this.formatDate(eadItem['RegistrationDate']);

      this.eadItems.push(ead);
    }
  }

  public formatDate(date) {
    const d = new Date(date);
    let month = '' + (d.getMonth() + 1);
    let day = '' + d.getDate();
    const year = d.getFullYear();

    if (month.length < 2) {
      month = '0' + month;
    }
    if (day.length < 2) {
      day = '0' + day;
    }

    return [year, month, day].join('-');
  }

  public convertJsonEADDPStagesToArray(res: any) {
    const arr = JSON.parse(res);
    for (const item of arr['ReturnExtraValues']) {
      this.EADDPStages.push(item);
    }
    const len = this.EADDPStages[0].length;
    this.EADDPStages[0][len.toString()] = 'Closed';
  }

  public logout() {
    this.cookieService.delete('logindata');
    this.loginService.setLoggedIn(false);
    this.router.navigateByUrl('/login');
  }

}

Right now what happens exactly is on pageload, it will do the "onSelectionChange" event for every item it loads into "EADDPStages[0]" !! So it will load, go for first item, then come in the applySelectFilter method, then it sees it has second option, will again go in the applySelectFilter method, third item etc... It will do this until the page is fully loaded, then it seemingly no filter applied. When I select an option from the mat-select, it will again loop through all the values while all I want is for it to apply the filter based on the value I have selected!

Does anyone see the potential problem?

Answer

Joshua Kemmerer picture Joshua Kemmerer · Jun 28, 2018

I believe (onSelectionChange) is deprecated as of Angular 5. I recommend using (selectionChange) according to the Angular Material 5 Docs. With that method, you can detect when the user makes a selection and apply the filter with the selected item.

In your markup:

<div class="filterSelectContainer" *ngIf="showDataForm">
  <mat-select placeholder="Search EADDPStage" (selectionChange)="selectStage($event)">
    <mat-option *ngFor="let stage of EADDPStages[0]" [value]="stage">
      {{stage}}
    </mat-option>
  </mat-select>
</div>

In your code-behind:

selectStage(event) {
  applyFilter(event.value);
}