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

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>

问题分析

  1. Observable处理逻辑错误:你使用的是RxJS的filter操作符,它的作用是过滤Observable流中的数据项,而非数组元素。如果dataSource$发出的是单个对象而非数组,RxJS的filter会直接返回该对象,导致subscribe里的data是单个对象,赋值给SearchDataValue后变成非数组,*ngFor无法遍历。
  2. 模板重复调用函数:模板中多次调用getrowtoDisplay(),每次都会重新订阅Observable,既造成性能浪费,又因异步特性,函数返回SearchDataValue时可能还未完成赋值,初始值可能不是数组。
  3. 类型定义不准确: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:10:11