Angular 14中Angular Material Table数据变更后无法刷新的求助
解决Angular Material Table增删改后不刷新的问题
问题根源
你当前自定义的TestTableDataSource中,connect()方法仅在初始化、分页切换、排序变化时推送数据。直接调用this.dataSource.data.pop()修改数组时,数据源并未发出新的数据流,表格无法感知数据变化——renderRows()只是强制重绘现有数据,无法触发数据源的更新逻辑,因此无效。
解决方案
方案一:改造自定义DataSource,添加数据更新触发器
在自定义数据源中引入Subject监听数据变化,每次修改数据后主动通知数据流更新:
- 修改
test-table-datasource.ts:
import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { map } from 'rxjs/operators'; import { Observable, of as observableOf, merge, Subject } from 'rxjs'; import { DataSource } from '@angular/cdk/table'; // 注意补充导入DataSource export interface TestTableItem { name: string; id: number; } const EXAMPLE_DATA: TestTableItem[] = [ {id: 1, name: 'Hydrogen'}, {id: 2, name: 'Helium'}, {id: 3, name: 'Lithium'}, {id: 4, name: 'Beryllium'}, {id: 5, name: 'Boron'}, {id: 6, name: 'Carbon'}, {id: 7, name: 'Nitrogen'}, {id: 8, name: 'Oxygen'}, {id: 9, name: 'Fluorine'}, {id: 10, name: 'Neon'}, {id: 11, name: 'Sodium'}, {id: 12, name: 'Magnesium'}, {id: 13, name: 'Aluminum'}, {id: 14, name: 'Silicon'}, {id: 15, name: 'Phosphorus'}, {id: 16, name: 'Sulfur'}, {id: 17, name: 'Chlorine'}, {id: 18, name: 'Argon'}, {id: 19, name: 'Potassium'}, {id: 20, name: 'Calcium'}, ]; export class TestTableDataSource extends DataSource<TestTableItem> { data: TestTableItem[] = EXAMPLE_DATA; paginator: MatPaginator | undefined; sort: MatSort | undefined; private dataUpdated = new Subject<void>(); // 新增数据更新触发器 constructor() { super(); } connect(): Observable<TestTableItem[]> { if (this.paginator && this.sort) { // 将dataUpdated合并到数据流中,触发数据更新 return merge(observableOf(this.data), this.paginator.page, this.sort.sortChange, this.dataUpdated) .pipe(map(() => { return this.getPagedData(this.getSortedData([...this.data])); })); } else { throw Error('Please set the paginator and sort on the data source before connecting.'); } } // 新增方法:修改数据后调用此方法触发更新 refreshData() { this.dataUpdated.next(); } disconnect(): void { this.dataUpdated.complete(); // 组件销毁时清理资源 } private getPagedData(data: TestTableItem[]): TestTableItem[] { if (this.paginator) { const startIndex = this.paginator.pageIndex * this.paginator.pageSize; return data.slice(startIndex, startIndex + this.paginator.pageSize); // 注意用slice代替splice,避免修改原数组 } else { return data; } } private getSortedData(data: TestTableItem[]): TestTableItem[] { 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); default: return 0; } }); } } function compare(a: string | number, b: string | number, isAsc: boolean): number { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); }
- 修改组件中的
removeData方法:
removeData(){ console.log(this.dataSource.data.length); this.dataSource.data.pop(); console.log(this.dataSource.data.length); this.dataSource.refreshData(); // 触发数据源更新 // 同步分页器长度 if(this.paginator){ this.paginator.length = this.dataSource.data.length; } }
方案二:改用MatTableDataSource(更简便)
如果不需要自定义复杂的数据源逻辑,直接使用Angular Material提供的MatTableDataSource,它内置了数据变化监听:
- 修改
test-table-component.ts:
import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTable, MatTableDataSource } from '@angular/material/table'; import { TestTableItem } from './test-table-datasource'; import { AfterViewInit, Component, ViewChild } from '@angular/core'; // 补充导入必要装饰器 @Component({ selector: 'app-test-table', templateUrl: './test-table.component.html', styleUrls: ['./test-table.component.css'] }) export class TestTableComponent implements AfterViewInit { @ViewChild(MatPaginator) paginator!: MatPaginator; @ViewChild(MatSort) sort!: MatSort; @ViewChild(MatTable) table!: MatTable<TestTableItem>; dataSource: MatTableDataSource<TestTableItem>; displayedColumns = ['id', 'name']; constructor() { const EXAMPLE_DATA: TestTableItem[] = [ {id: 1, name: 'Hydrogen'}, {id: 2, name: 'Helium'}, {id: 3, name: 'Lithium'}, {id: 4, name: 'Beryllium'}, {id: 5, name: 'Boron'}, {id: 6, name: 'Carbon'}, {id: 7, name: 'Nitrogen'}, {id: 8, name: 'Oxygen'}, {id: 9, name: 'Fluorine'}, {id: 10, name: 'Neon'}, {id: 11, name: 'Sodium'}, {id: 12, name: 'Magnesium'}, {id: 13, name: 'Aluminum'}, {id: 14, name: 'Silicon'}, {id: 15, name: 'Phosphorus'}, {id: 16, name: 'Sulfur'}, {id: 17, name: 'Chlorine'}, {id: 18, name: 'Argon'}, {id: 19, name: 'Potassium'}, {id: 20, name: 'Calcium'}, ]; this.dataSource = new MatTableDataSource(EXAMPLE_DATA); } ngAfterViewInit(): void { this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; } removeData(){ console.log(this.dataSource.data.length); this.dataSource.data.pop(); console.log(this.dataSource.data.length); // 通过重新赋值数组触发变更检测 this.dataSource.data = [...this.dataSource.data]; // 同步分页器长度 if(this.paginator){ this.paginator.length = this.dataSource.data.length; } } }
关键注意点
- 自定义DataSource必须通过可观察对象推送新数据,表格才会响应变化;
- 使用
MatTableDataSource时,避免直接修改数组(如pop/push),需通过重新赋值数组触发变更检测; - 分页器的
length需手动同步,否则数据变化后分页器不会自动更新。
内容的提问来源于stack exchange,提问作者Panter
相关产品推荐
相关产品推荐

