Angular中NgFor绑定报错:仅支持可迭代对象(如数组)
Angular项目中*ngFor循环触发NG02200错误的解决方法
问题场景
编写getrowtoDisplay函数处理Observable数据并存入SearchDataValue数组,在模板中用*ngFor循环该数组时触发错误。
错误信息(翻译后)
Error: NG02200: 无法找到支持类型为'Nikhil'的对象'[object Object]'的差异器。NgFor仅支持绑定到可迭代对象,例如数组。是否想要使用keyvalue管道?
相关代码
TypeScript函数代码
protected getrowtoDisplay(): any { let value = (this.searchEvent); if (value) { var rowFiltered: Observable < any[] > = this.dataSource$.pipe( filter(x => (x.name.toLowerCase()).match(value.toLowerCase()) && x.hide !== true), ); console.log("enterd first"); rowFiltered.subscribe(data => { this.SearchDataValue = data; //console.log(this.SearchDataValue); }) //var rowFiltered = this.dataSource$.filter(x => (x.name.toLowerCase()).match(value.toLowerCase()) && x.hide !==true ); } else { var rowFiltered: Observable < any[] > = this.dataSource$.pipe( filter(x => (x.hide !== true), )); rowFiltered.subscribe(data => { this.SearchDataValue = data; //console.log(this.SearchDataValue); }) //var rowFiltered = this.dataSource$.filter(x => x.hide !== true); } return this.SearchDataValue; }
HTML模板代码
<tbody *ngIf="this.getrowtoDisplay().length!==0;else other_content" class="xyz"> <tr *ngFor="let row of this.getrowtoDisplay() "> <td *ngFor="let column of this.getColumnsToDisplay()"> <div > {{ row[column.columnDef] }} </div> </td> </tr> </tbody> <ng-template #other_content> <tbody class="abc"> <tr > <td > <div > No data to display </div> </td> </tr> </tbody> </ng-template>
问题分析
- Observable处理逻辑错误:你使用的是RxJS的
filter操作符,它的作用是过滤Observable流中的数据项,而非数组元素。如果dataSource$发出的是单个对象而非数组,RxJS的filter会直接返回该对象,导致subscribe里的data是单个对象,赋值给SearchDataValue后变成非数组,*ngFor无法遍历。 - 模板重复调用函数:模板中多次调用
getrowtoDisplay(),每次都会重新订阅Observable,既造成性能浪费,又因异步特性,函数返回SearchDataValue时可能还未完成赋值,初始值可能不是数组。 - 类型定义不准确:
rowFiltered被定义为Observable<any[]>,但实际如果dataSource$发出单个对象,这个定义就不匹配,导致后续赋值错误。
解决方案
方案1:用async管道处理Observable(推荐)
直接在组件中维护过滤后的Observable,通过async管道在模板中自动订阅,避免手动订阅的问题:
修改TypeScript代码
// 组件中定义过滤后的数据源Observable filteredData$: Observable<any[]>; // 假设searchEvent是Observable,若不是可转成BehaviorSubject searchEvent$: BehaviorSubject<string> = new BehaviorSubject(''); ngOnInit(): void { // 组合数据源和搜索关键词,自动更新过滤结果 this.filteredData$ = combineLatest([this.dataSource$, this.searchEvent$]).pipe( map(([data, searchValue]) => { // 确保数据是数组,单个对象转成数组 const dataArray = Array.isArray(data) ? data : [data]; if (searchValue) { return dataArray.filter(x => x.name.toLowerCase().includes(searchValue.toLowerCase()) && !x.hide ); } else { return dataArray.filter(x => !x.hide); } }) ); } // 更新搜索关键词的方法 updateSearchEvent(value: string): void { this.searchEvent$.next(value); }
修改HTML模板
<tbody *ngIf="(filteredData$ | async)?.length !==0;else other_content" class="xyz"> <tr *ngFor="let row of filteredData$ | async"> <td *ngFor="let column of getColumnsToDisplay()"> <div> {{ row[column.columnDef] }} </div> </td> </tr> </tbody> <ng-template #other_content> <tbody class="abc"> <tr> <td> <div> No data to display </div> </td> </tr> </tbody> </ng-template>
方案2:修复手动订阅逻辑
若坚持手动订阅,需确保SearchDataValue始终为数组,并避免模板重复调用函数:
修改TypeScript代码
SearchDataValue: any[] = []; // 初始化为空数组 destroy$: Subject<void> = new Subject<void>(); // 用于销毁订阅,避免内存泄漏 ngOnInit(): void { // 初始化时执行过滤,后续可监听searchEvent变化重新执行 this.updateFilteredData(); } // 统一处理过滤逻辑 private updateFilteredData(): void { let value = this.searchEvent; this.dataSource$.pipe( takeUntil(this.destroy$), // 组件销毁时取消订阅 map(data => { const dataArray = Array.isArray(data) ? data : [data]; if (value) { return dataArray.filter(x => x.name.toLowerCase().includes(value.toLowerCase()) && !x.hide ); } else { return dataArray.filter(x => !x.hide); } }) ).subscribe(data => { this.SearchDataValue = data; }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); }
修改HTML模板
<tbody *ngIf="SearchDataValue.length !==0;else other_content" class="xyz"> <tr *ngFor="let row of SearchDataValue"> <td *ngFor="let column of getColumnsToDisplay()"> <div> {{ row[column.columnDef] }} </div> </td> </tr> </tbody> <ng-template #other_content> <tbody class="abc"> <tr> <td> <div> No data to display </div> </td> </tr> </tbody> </ng-template>
关键注意点
- 区分RxJS的
filter操作符和数组的filter方法:前者过滤Observable流数据,后者过滤数组元素。 - 避免在模板中频繁调用函数:每次变更检测都会执行,导致重复订阅和性能问题。
- 确保*ngFor绑定的是可迭代对象(数组、Set等),单个对象需转成数组或使用
keyvalue管道。
内容的提问来源于stack exchange,提问作者Shabasy
相关产品推荐
相关产品推荐

