Angular中自己的DataTable如何实现过滤?

How to implement filtering in my own DataTable in Angular?

我在使用过滤器时遇到问题。我决定实现我的个人数据源而不是使用 MatTableDataSource,因此我没有可用的过滤方法。我可以从 HTML 捕获事件,然后将其传递给组件,然后从组件将其传递给数据源。唯一的问题是,即使接收到过滤器,table 也不会更新,就好像没有我不知道如何实现的事件发射器一样。例如,如果我插入过滤器并更改页面顺序,过滤器就会起作用。我将 post 代码以获得更好的外观,对于发音我很抱歉

数据模型

import { Sistema } from './sistema'

export class Galassia {

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

}

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

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

如果我关于过滤器未在您的连接中触发任何新观察值的评论是正确的,您可以使用类似这样的东西:

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