How to implement filtering in my own DataTable in Angular?

走远了吗. 提交于 2020-07-23 06:45:57

问题


I'm having problems with filters. I decided to implement my personal datasource and not to use the MatTableDataSource and therefore I don't have the filter method available. I can capture the event from the HTML and then pass it to the component and from the component pass it to the datasource. The only problem is that even if the filter is received, the table does not update as if there were no event emitter that I don't know how to implement. For example, if I insert the filter and change the page order, the filter works. I'll post the code to get a better look and I'm sorry for the pronunciation

data model

import { Sistema } from './sistema'

export class Galassia {

  id: number;
  nome: string;
  imgLink: string;
  sistema: Sistema[];

}

service-rest.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Galassia } from '../model/galassia';
import { catchError, tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ServizioProdottiRESTService {


  galassie: Galassia[];

  prodottiUrl = 'http://localhost:8080/prodotti';

  constructor(private http: HttpClient) { }

  getGalassie(): Observable<Galassia[]> {
    return this.http.get<Galassia[]>(this.prodottiUrl + '/galassie').pipe(
    tap(galaxy => this.galassie = galaxy),
    catchError(this.handleError('getGalassie', []))
  );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
        console.log(error);
        this.log(`${operation} failed: ${error.message}`);
        return of(result as T);
    };
  }

  private log(message: string) {
    console.log('HeroService: ' + message);
  }
}

component.ts

import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable } from '@angular/material/table';
import { ListaProdottiDataSource } from './lista-prodotti-datasource';
import { ServizioProdottiRESTService } from '../servizio-prodotti-rest.service';
import { Galassia } from '../../model/galassia';

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

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;
  @ViewChild(MatTable) table: MatTable<Galassia>;

  dataSource: ListaProdottiDataSource;
  displayedColumns = ['imgLink' ,'nome', 'button'];

  constructor(private myService: ServizioProdottiRESTService) {}

  ngOnInit() {
    this.dataSource = new ListaProdottiDataSource(this.myService);
  }

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

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }
}

component.html

<div class="example-header">
  <mat-form-field>
    <input matInput (keyup)="applyFilter($event)" placeholder="Ex. ium">
  </mat-form-field>
</div>

<div class="mat-elevation-z8 table">

  <table mat-table class="full-width-table" matSort aria-label="Elements".[dataSource]="dataSource.data">

    <!-- imgLink Column -->
    <ng-container matColumnDef="imgLink">
      <th mat-header-cell *matHeaderCellDef mat-sort-header>Img</th>
      <td mat-cell *matCellDef="let row"><img [src]="row.imgLink"/></td>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="nome">
      <th mat-header-cell *matHeaderCellDef mat-sort-header>Nome</th>
      <td mat-cell *matCellDef="let row">{{row.nome}}</td>
    </ng-container>

    <!-- Button Column -->
    <ng-container matColumnDef="button">
      <th mat-header-cell *matHeaderCellDef mat-sort-header>Button</th>
      <td mat-cell *matCellDef="let row"><button mat-raised-button color="primary">Scegli ></button></td>
    </ng-container>

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

  <mat-paginator #paginator
      [length]="dataSource?.data.length"
      [pageIndex]="0"
      [pageSize]="5"
      [pageSizeOptions]="[5, 10, 15]">
  </mat-paginator>
</div>

and datasource.ts

import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { map } from 'rxjs/operators';
import { Observable, of as observableOf, merge } from 'rxjs';
import { ServizioProdottiRESTService } from '../servizio-prodotti-rest.service';
import { Galassia } from '../../model/galassia';


export class ListaProdottiDataSource extends DataSource<Galassia> {

  data: Galassia[];
  paginator: MatPaginator;
  sort: MatSort;
  filter: string;

  constructor(private myService: ServizioProdottiRESTService) {
    super();
    this.myService.getGalassie().subscribe(galaxy => this.data = galaxy);
  }


  connect(): Observable<Galassia[]> {

    const dataMutations = [
      observableOf(this.data),
      this.paginator.page,
      this.sort.sortChange
    ];

    return merge(...dataMutations).pipe(map(() => {
      return this.getFilteredData(this.getPagedData(this.getSortedData([...this.data])));
    }));
  }

  disconnect() {}

  private getFilteredData(data: Galassia[]) {

    return data.filter(d => d.nome.toLowerCase() == this.filter);
  }

  private getPagedData(data: Galassia[]) {
    const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
    return data.splice(startIndex, this.paginator.pageSize);
  }

  private getSortedData(data: Galassia[]) {
    if (!this.sort.active || this.sort.direction === '') {
      return data;
    }

    return data.sort((a, b) => {
      const isAsc = this.sort.direction === 'asc';
      switch (this.sort.active) {
        case 'nome': return compare(a.nome, b.nome, isAsc);
        default: return 0;
      }
    });
  }
}

function compare(a: string | number, b: string | number, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

回答1:


If my comment is correct about the filter not trigging any new observables in your connect, you could use something maybe like this:

import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { map } from 'rxjs/operators';
import { Observable, of as observableOf, merge, BehaviorSubject } from 'rxjs';
import { ServizioProdottiRESTService } from '../servizio-prodotti-rest.service';
import { Galassia } from '../../model/galassia';


export class ListaProdottiDataSource extends DataSource<Galassia> {

  data: Galassia[];
  paginator: MatPaginator;
  sort: MatSort;
  get filter(): string { return this.filter$.getValue(); }
  set filter(value: string) { this.filter$.next(value); }
  filter$: BehaviorSubject<string> = new BehaviorSubject<string>(null);

  constructor(private myService: ServizioProdottiRESTService) {
    super();
    this.myService.getGalassie().subscribe(galaxy => this.data = galaxy);
  }


  connect(): Observable<Galassia[]> {

    const dataMutations = [
      observableOf(this.data),
      this.paginator.page,
      this.sort.sortChange,
      this.filter$
    ];

    return merge(...dataMutations).pipe(map(() => {
      return this.getFilteredData(this.getPagedData(this.getSortedData([...this.data])));
    }));
  }

  disconnect() {}

  private getFilteredData(data: Galassia[]) {

    return data.filter(d => d.nome.toLowerCase() == this.filter);
  }

  private getPagedData(data: Galassia[]) {
    const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
    return data.splice(startIndex, this.paginator.pageSize);
  }

  private getSortedData(data: Galassia[]) {
    if (!this.sort.active || this.sort.direction === '') {
      return data;
    }

    return data.sort((a, b) => {
      const isAsc = this.sort.direction === 'asc';
      switch (this.sort.active) {
        case 'nome': return compare(a.nome, b.nome, isAsc);
        default: return 0;
      }
    });
  }
}

function compare(a: string | number, b: string | number, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}


来源:https://stackoverflow.com/questions/62236894/how-to-implement-filtering-in-my-own-datatable-in-angular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!