首页 > 解决方案 > «Cannot find a different support object '[object Object]'» with Observable when `*ngFor="let item of items | async" `

问题描述

我遇到了一些我不理解 Angular 的错误。因此,经过多次不成功的尝试,我来到了这里。

似乎Observable<Carte[]>被称为objectby *ngFor(似乎合法),但我发现的所有样本都只是 | async用来管理它。就我而言,它失败了。

当我添加了 firestore 层来管理我的数据时发生了这个错误。顺便说一句,构造函数中的日志转储了这些数据:

[{youtubeID: "dummy", rows: 1, cols: 1},{cols: 2, rows: 2, youtubeID: "an3yLsgQVAQ"}]

环境。

Windows 上的 Ubuntu (wsl2)

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1102.10
@angular-devkit/build-angular   0.1102.10
@angular-devkit/core            11.2.10
@angular-devkit/schematics      11.2.10
@angular/cdk                    11.2.10
@angular/cli                    11.2.10
@angular/fire                   6.1.4
@angular/material               11.2.10
@schematics/angular             11.2.10
@schematics/update              0.1102.10
rxjs                            6.6.7
typescript                      4.1.5

错误描述

当我尝试 *ngFor 一些带有异步管道的 observable 时,这个错误会在我的控制台中弹出。

core.js:6210 ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

零件

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Component } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { Carousel } from './carousel/carousel.interface';
import { Dashboard } from './dashboard.interface';
import { Youtube } from './youtube/youtube.interface';

type Carte = ((Carousel | Youtube) & Dashboard);

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent {
  cartesStandards: Observable<Carte[]> = this.firestore.collection<Carte>('rubriques').valueChanges();

  /** Based on the screen size, switch from standard to one column per row */
  cards: Observable<Carte[]> = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
    switchMap(matches => matches ? this.cartesAjustees : this.cartesStandards)
  );


  get cartesAjustees() {
    return this.calculeCarteAjustees()
  }


  constructor(
    private breakpointObserver: BreakpointObserver,
    private firestore: AngularFirestore
  ) {
    this.cartesStandards.subscribe(data=>console.log(data))
  }



  private calculeCarteAjustees() {
    return this.cartesStandards.pipe<Carte[]>(map(carte => ({ ...carte, cols: 1, rows: 1 })));
  }

  castVersCarousel(carte: Carte) {
    if (!this.estUnCarousel(carte)) {
      console.debug(carte)
      throw new Error("impossible de transformer en carousel")
    }
    return carte as Carousel;
  }

  castVersYoutube(carte: Carte) {
    if (!this.estUneVideoYoutube(carte)) {
      console.debug(carte)
      throw new Error("impossible de transformer en video youtube")
    }
    return carte as Youtube;
  }

  estUnCarousel(carte: Carte) {
    return "slides" in carte && "animationType" in carte;
  }

  estUneVideoYoutube(carte: Carte) {
    return "youtubeID" in carte
  }
}
<div class="grid-container">
  <h1 class="mat-h1">Services</h1>
  <mat-grid-list cols="2" rowHeight="350px">
    <mat-grid-tile *ngFor="let card of cards | async" [colspan]="card.cols" [rowspan]="card.rows">
       <mat-card class="dashboard-card">

        <ng-template  [ngIf]="estUnCarousel(card)" [ngIfElse]="youtube">
          <carousel [slides]="castVersCarousel(card).slides" [animationType]="castVersCarousel(card).animationType">
          </carousel>
        </ng-template>

        <ng-template #youtube>
          <youtube [youtubeID]="castVersYoutube(card).youtubeID"></youtube>
        </ng-template>

      </mat-card>
    </mat-grid-tile>
  </mat-grid-list>
</div>

标签: angularasynchronousobservablengfor

解决方案


似乎该switchMap操作导致错误的演员表(参见 Dmitry S. remark)。由于cartesStandards正在转储一个数组,我确信卡片应该通过推理转储一个数组。

卡片实际上是一个对象

我正在寻找如何解决这种情况。

编辑

这是最终的解决方案,可能不像我想要的那么干净,但就像一个魅力。

我添加了一些重构,并延迟colsrows更新 html 组件以避免嵌套 Observable。

所以这里的代码

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Component } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Carousel } from './carousel/carousel.interface';
import { Dashboard } from './dashboard.interface';
import { Youtube } from './youtube/youtube.interface';


export type Carte = ((Carousel | Youtube) & Dashboard)

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent {
  cartesStandards = this.firestore.collection<Carte>('rubriques').valueChanges()

  /** Based on the screen size, switch from standard to one column per row */
  estUnPetitEcran: Observable<boolean> = this
    .breakpointObserver
    .observe(Breakpoints.Handset)
    .pipe(map(bp => bp.matches))

  constructor(
    private breakpointObserver: BreakpointObserver,
    private firestore: AngularFirestore
  ) { }

  castVersCarousel(carte: Carte) {
    if (!this.estUnCarousel(carte)) {
      console.debug(carte)
      throw new Error('impossible de transformer en carousel')
    }
    return carte as Carousel
  }

castVersYoutube(carte: Carte) {
    if (!this.estUneVideoYoutube(carte)) {
      console.debug(carte)
      throw new Error('impossible de transformer en video youtube')
    }
    return carte as Youtube
  }

estUnCarousel(carte: Carte) {
    return 'slides' in carte && 'animationType' in carte
  }

estUneVideoYoutube(carte: Carte) {
    return 'youtubeID' in carte
  }
}
<div class="grid-container">
  <h1 class="mat-h1">Services</h1>
  <mat-grid-list cols="2" rowHeight="350px">
    <mat-grid-tile *ngFor="let carte of cartesStandards | async" [colspan]="(estUnPetitEcran | async) ? 1 : carte.cols" [rowspan]="(estUnPetitEcran | async) ? 1 : carte.rows">
       <mat-card class="dashboard-card">

        <ng-template  [ngIf]="estUnCarousel(carte)" [ngIfElse]="youtube">
          <carousel [slides]="castVersCarousel(carte).slides" [animationType]="castVersCarousel(carte).animationType">
          </carousel>
        </ng-template>

        <ng-template #youtube>
          <youtube [youtubeID]="castVersYoutube(carte).youtubeID"></youtube>
        </ng-template>

      </mat-card>
    </mat-grid-tile>
  </mat-grid-list>
</div>

推荐阅读