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

Angular Material Mat-table无法显示REST API数据,分页排序失效

Fixing Mat-table Pagination & Sorting Issues with Async Data

Let's break down what's going wrong with your implementation and fix it step by step:

Key Issues in Your Code

  1. ViewChild Timing: You're assigning paginator and sort in ngOnInit, but with static: false, these elements aren't available until the view is initialized (use ngAfterViewInit instead).
  2. Data Updates Not Triggering Table Refresh: Your DataSource uses observableOf(this.data) which only emits once. When async data arrives later, the table has no way to detect the change.
  3. Paginator Length Not Updated: The paginator's length is set only once in connect, so it doesn't reflect the actual data count after the API call.
  4. Unnecessary Observable Wrapping: Your service's getAllAccounts method uses Observable.create unnecessarily, which adds complexity.

Fixed Code Implementations

1. Updated AccountListDataSource

We'll use a BehaviorSubject to track data changes and ensure the table gets notified when new data arrives:

import { DataSource } from '@angular/cdk/collections';
import { Observable, BehaviorSubject, merge, EMPTY } from 'rxjs';
import { map } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { User } from './your-user-model-path'; // Replace with your actual User model path

export class AccountListDataSource extends DataSource<User> {
  private dataSubject = new BehaviorSubject<User[]>([]);
  private _data: User[] = [];

  paginator: MatPaginator;
  sort: MatSort;

  constructor() {
    super();
  }

  // Setter to update data and notify subscribers
  set data(data: User[]) {
    this._data = data;
    this.dataSubject.next(data);
    // Update paginator length when data changes
    if (this.paginator) {
      this.paginator.length = data.length;
    }
  }

  get data(): User[] {
    return this._data;
  }

  connect(): Observable<User[]> {
    // Combine data changes, paginator events, and sort events
    const dataMutations = [
      this.dataSubject.asObservable(),
      this.paginator?.page || EMPTY,
      this.sort?.sortChange || EMPTY
    ];

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

  disconnect() {
    this.dataSubject.complete();
  }

  // Helper for sorting
  private getSortedData(data: User[]): User[] {
    if (!this.sort || !this.sort.active || this.sort.direction === '') {
      return data;
    }

    return data.sort((a, b) => {
      const isAsc = this.sort.direction === 'asc';
      switch (this.sort.active) {
        case 'name': return compare(a.name, b.name, isAsc);
        case 'id': return compare(+a.id, +b.id, isAsc);
        // Add other columns as needed
        default: return 0;
      }
    });
  }

  // Helper for pagination
  private getPagedData(data: User[]): User[] {
    if (!this.paginator) {
      return data;
    }
    const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
    return data.slice(startIndex, startIndex + this.paginator.pageSize);
  }
}

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

2. Updated AccountListComponent

Move paginator/sort assignment to ngAfterViewInit and ensure async data updates trigger table refreshes:

import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable } from '@angular/material/table';
import { AccountListDataSource } from './account-list-datasource';
import { AccountManagementService } from './account-management.service';
import { User } from './your-user-model-path'; // Replace with your model path

@Component({
  selector: 'app-account-list',
  templateUrl: './account-list.component.html',
  styleUrls: ['./account-list.component.css']
})
export class AccountListComponent implements OnInit, AfterViewInit {
  @ViewChild(MatPaginator, {static: false}) paginator: MatPaginator;
  @ViewChild(MatSort, {static: false}) sort: MatSort;
  @ViewChild(MatTable, {static: false}) table: MatTable<User>;
  dataSource: AccountListDataSource;

  displayedColumns = ['id', 'name'];

  constructor(private accountManagementService: AccountManagementService) { }

  ngOnInit() {
    this.dataSource = new AccountListDataSource();
    this.getUsers();
  }

  ngAfterViewInit() {
    // Assign paginator and sort once they're available
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
    // Trigger table render if needed
    this.table.renderRows();
  }

  getUsers() {
    this.accountManagementService.getAllAccounts().subscribe((response: any) => {
      this.dataSource.data = response.content;
      console.log('Loaded data:', this.dataSource.data); // This will now show your data
    }, error => {
      console.error('Failed to fetch users:', error);
    });
  }
}

3. Simplified AccountManagementService

Remove the unnecessary Observable.create wrapper:

import { Observable } from 'rxjs';
import { ApiService } from './api.service'; // Your existing ApiService

export class AccountManagementService {
  private PREFIX = 'accounts'; // Replace with your actual prefix

  constructor(private apiService: ApiService) { }

  getAllAccounts(): Observable<any> {
    return this.apiService.get({ endPoint: `/${this.PREFIX}/` });
  }
}

Why These Fixes Work

  • BehaviorSubject: Tracks changes to your data and emits new values whenever the data is updated, ensuring the table refreshes automatically.
  • ngAfterViewInit: Ensures paginator and sort are fully initialized before assigning them to the data source.
  • Paginator Length Update: The data setter updates the paginator's length whenever new data arrives, so pagination works correctly.
  • Simplified Service: Removes redundant code and leverages the existing observable from your ApiService.

Note: The reason you saw undefined when logging this.dataSource.data right after this.getUsers() is because the API call is asynchronous— the subscribe callback runs after ngOnInit completes. Logging inside the subscribe (as shown) will show the correct data once it's loaded.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:57:38