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