Angular10使用MatTable分页器出现多个Cannot read property 'length' of undefined错误如何解决
错误原因
你遇到的报错核心来自两个问题:
- 自定义DataSource的
data属性没有初始化默认值,API异步请求返回数据前data为undefined,此时模板、组件逻辑里访问data.length就会抛出空指针错误。 - 数据流设计不合理:
connect方法中使用observableOf(this.data)只能传递初始的静态值,API返回新数据后无法触发表格的更新流,即使数据赋值成功也不会主动触发重渲染(虽然你现在能看到数据可能是其他巧合触发了变更检测,但这是不稳定的)。
修复方案
方案1:调整自定义DataSource实现
修改datasource.ts代码
import { BehaviorSubject } from 'rxjs'; export class ListClientDataSource extends DataSource<Client> { // 替换为响应式的BehaviorSubject,初始化空数组避免undefined private dataSubject = new BehaviorSubject<Client[]>([]); paginator: MatPaginator; sort: MatSort; constructor(public clientService: ClientService) { super(); this.clientService.getClientsList().subscribe(result =>{ // 数据返回后推送新值到数据流 this.dataSubject.next(result.data); }); } // 对外暴露data值供模板读取 get data() { return this.dataSubject.value; } connect(): Observable<Client[]> { const dataMutations = [ // 用响应式数据流代替静态的observableOf this.dataSubject.asObservable(), this.paginator.page, this.sort.sortChange ]; return merge(...dataMutations).pipe(map(() => { // 从subject中取最新值做分页排序 return this.getPagedData(this.getSortedData([...this.dataSubject.value])); })); } // 补充disconnect方法清理订阅,防止内存泄漏 disconnect(): void { this.dataSubject.complete(); } // 保留你原有的getPagedData、getSortedData方法即可,下面是示例实现 private getPagedData(data: Client[]): Client[] { const startIndex = this.paginator.pageIndex * this.paginator.pageSize; return data.splice(startIndex, this.paginator.pageSize); } private getSortedData(data: Client[]): Client[] { 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 'name': return compare(a.name, b.name, isAsc); case 'id': return compare(+a.id, +b.id, isAsc); default: return 0; } }); } } // 排序工具函数 function compare(a: number | string, b: number | string, isAsc: boolean) { return (a < b ? -1 : 1) * (isAsc ? 1 : -1); }
其余代码无需修改
- component.ts的逻辑保持不变即可,因为
dataSubject.value默认是空数组,ngAfterViewInit中访问length也不会报错 - 模板的
[length]="dataSource?.data.length"无需调整,已经可以正常工作
方案2:使用Angular官方封装的MatTableDataSource(更简单)
如果没有特殊的自定义数据流需求,直接用官方提供的MatTableDataSource可以省去大量自定义逻辑,无需自己实现DataSource:
修改component.ts代码
import { MatTableDataSource } from '@angular/material/table'; export class ListClientComponent implements AfterViewInit, OnInit { @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; // 直接使用官方数据源 dataSource = new MatTableDataSource<Client>(); constructor(public clientService: ClientService) { } displayedColumns = ['id', 'name', 'contact', 'email', 'date', 'iot', 'loc']; ngOnInit() { this.clientService.getClientsList().subscribe(result =>{ this.dataSource.data = result.data; }); } ngAfterViewInit() { this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; } }
修改模板代码
<mat-paginator #paginator [length]="dataSource.data.length" [pageIndex]="0" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]"> </mat-paginator>
这个方案不需要自定义ListClientDataSource类,所有分页、排序、过滤逻辑官方都已经封装好,更稳定易维护。
内容的提问来源于stack exchange,提问作者Ashwin Allen
相关产品推荐
相关产品推荐

