You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决@angular/material表格排序及过滤功能异常问题

问题:Angular Material表格排序与过滤功能异常

异常表现

  • 排序问题:仅对当前分页数据做前端排序,切换页面后新页面数据仍在自身范围内排序,未请求服务器返回全量数据排序后的分页结果。
  • 过滤问题:仅能搜索当前页存在的内容,无法匹配全量数据库中其他页的数据;清除过滤条件后,总条数变为默认页大小(5),而非数据库总条数。

现有代码

组件代码

import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { CommonModule } from '@angular/common';
import { TroData } from './regulation-orders-table.model';
import { RegulationOrdersTableService } from './regulation-orders-table.service';
import { MatTableDataSource } from '@angular/material/table';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { EventEmitter, Output, ViewChild, inject } from '@angular/core';

@Component({
  selector: 'app-traffic-regulation-orders',
  standalone: true,
  imports: [CommonModule, MatPaginatorModule, MatTableModule, MatSortModule, MatFormFieldModule, MatInputModule],
  templateUrl: './regulation-orders-table.component.html',
  styleUrls: ['./regulation-orders-table.component.css']
})
export class TrafficRegulationOrdersComponent {
  regulationOrdersTableService: RegulationOrdersTableService = inject(RegulationOrdersTableService);
  tableData: TroData[] = [];
  pageSize: number = 5;
  pageIndex: number = 0;
  totalRecords: number = 0;
  displayedColumns: string[] = ['externalId', 'expectedImpact', 'description', 'isPermanentChange', 'locationType', 'locationData'];

  @Output() selectedTroEmitter = new EventEmitter<string>();

  dataSource: MatTableDataSource<TroData> = new MatTableDataSource<TroData>([]);

  @ViewChild(MatSort) sort!: MatSort;
  @ViewChild(MatPaginator) paginator!: MatPaginator;

  constructor() {
    this.fetchData();
  }

  fetchData(): void {
    const currentPage = this.pageIndex + 1;

    console.log('Fetching data for page:', currentPage);

    this.regulationOrdersTableService.getTroList(currentPage, this.pageSize).then((response: { items: TroData[], totalCount: number }) => {
      this.tableData = response.items;
      this.totalRecords = response.totalCount;

      this.dataSource.data = this.tableData;
      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;
      setTimeout(() => {
        this.paginator.pageIndex = this.pageIndex;
        this.paginator.length = this.totalRecords;
      });

    }).catch((error) => {
      console.error('Error loading table data', error);
    });
  }

  onRowClick(data: TroData): void {
    this.selectedTroEmitter.emit(data.externalId);
  }

  onPageChange(event: any): void {
    this.pageSize = event.pageSize;
    this.pageIndex = event.pageIndex;
    this.fetchData();
  }

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

HTML代码

<mat-form-field>
  <mat-label>Filter</mat-label>
  <input matInput (keyup)="applyFilter($event)" placeholder="Search" />
</mat-form-field>

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
  <ng-container matColumnDef="externalId">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>External ID</th>
    <td mat-cell *matCellDef="let row">{{ row.externalId }}</td>
  </ng-container>

  <ng-container matColumnDef="expectedImpact">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Expected Impact</th>
    <td mat-cell *matCellDef="let row">{{ row.expectedImpact }}</td>
  </ng-container>

  <ng-container matColumnDef="description">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Description</th>
    <td mat-cell *matCellDef="let row">{{ row.description }}</td>
  </ng-container>

  <ng-container matColumnDef="isPermanentChange">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Permanent Change</th>
    <td mat-cell *matCellDef="let row">{{ row.isPermanentChange }}</td>
  </ng-container>

  <ng-container matColumnDef="locationType">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Location Type</th>
    <td mat-cell *matCellDef="let row">{{ row.locationType }}</td>
  </ng-container>

  <ng-container matColumnDef="locationData">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Location Data</th>
    <td mat-cell *matCellDef="let row">{{ row.locationData }}</td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>

  <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="onRowClick(row)" class="clickable-row"></tr>
</table>

<mat-paginator [length]="totalRecords"
               [pageSize]="pageSize"
               [pageIndex]="pageIndex"
               [pageSizeOptions]="[5, 10, 20]"
               showFirstLastButtons
               aria-label="Select page"
               (page)="onPageChange($event)">
</mat-paginator>

服务代码

import { Injectable } from '@angular/core';
import { TroData } from './regulation-orders-table.model';

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

  async getTroList(currentPage: number, pageSize: number): Promise<{ items: TroData[], totalCount: number }> {
    const response = await fetch('/api/traffic-regulation-orders/list', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        page: currentPage,
        pageSize: pageSize,
      }),
    });

    if (!response.ok) {
      throw new Error('Failed to fetch data');
    }

    const data = await response.json();
    return { items: data.items, totalCount: data.totalCount };
  }

  async getTroById(externalId: string): Promise<TroData> {
    const data = await fetch(`/api/traffic-regulation-orders/load/${externalId}`);
    return await data.json();
  }
}

修复方案

排序问题修复

当前排序仅在前端对分页数据生效,需改为服务器端排序:

  1. 监听MatSort的sortChange事件,捕获排序字段和方向
  2. 修改数据请求逻辑,将排序参数传递给后端
  3. 后端接收排序参数,对全量数据排序后再分页返回

过滤问题修复

当前过滤仅在前端对分页数据生效,需改为服务器端过滤:

  1. 维护过滤条件变量,过滤变化时触发服务器请求
  2. 修改数据请求逻辑,将过滤参数传递给后端
  3. 后端接收过滤参数,返回符合条件的数据及总条数
  4. 清除过滤时重置参数,重新请求全量数据的分页结果

修改后的代码

组件代码(关键修改)

import { ..., AfterViewInit } from '@angular/core';

export class TrafficRegulationOrdersComponent implements AfterViewInit {
  // 新增排序、过滤参数
  sortColumn: string = '';
  sortDirection: 'asc' | 'desc' | '' = '';
  filterValue: string = '';

  // 初始化后监听排序变化
  ngAfterViewInit() {
    this.sort.sortChange.subscribe((sortState) => {
      this.sortColumn = sortState.active;
      this.sortDirection = sortState.direction;
      this.pageIndex = 0; // 排序后回到第一页
      this.fetchData();
    });
  }

  // 修改fetchData,传递排序、过滤参数
  fetchData(): void {
    const currentPage = this.pageIndex + 1;

    this.regulationOrdersTableService.getTroList(currentPage, this.pageSize, this.sortColumn, this.sortDirection, this.filterValue)
      .then((response: { items: TroData[], totalCount: number }) => {
        this.tableData = response.items;
        this.totalRecords = response.totalCount;
        this.dataSource.data = this.tableData;
        // 移除重复设置paginator和sort,避免多次初始化
        setTimeout(() => {
          this.paginator.pageIndex = this.pageIndex;
          this.paginator.length = this.totalRecords;
        });
      }).catch((error) => {
        console.error('Error loading table data', error);
      });
  }

  // 修改applyFilter,触发服务器请求而非前端过滤
  applyFilter(event: Event): void {
    this.filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
    this.pageIndex = 0; // 过滤后回到第一页
    this.fetchData();
  }
}

服务代码(关键修改)

async getTroList(
  currentPage: number, 
  pageSize: number, 
  sortColumn?: string, 
  sortDirection?: string, 
  filterValue?: string
): Promise<{ items: TroData[], totalCount: number }> {
  const response = await fetch('/api/traffic-regulation-orders/list', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      page: currentPage,
      pageSize: pageSize,
      sortColumn,
      sortDirection,
      filter: filterValue
    }),
  });

  if (!response.ok) {
    throw new Error('Failed to fetch data');
  }

  const data = await response.json();
  return { items: data.items, totalCount: data.totalCount };
}

后端调整说明

后端接口/api/traffic-regulation-orders/list需要新增处理逻辑:

  • 接收sortColumn、sortDirection参数,对全量数据执行排序
  • 接收filter参数,根据业务需求对数据执行过滤(如匹配指定字段)
  • 返回过滤、排序后的分页数据,以及对应的总条数(过滤后的总记录数)

内容的提问来源于stack exchange,提问作者Dinislam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:47:36