Home > other >  Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor
Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor

Time:08-18

I have a little problem with my code. I have 2 HTTP requests and after receiving the data I merge them together but when I try to loop through the final array I get the error displayed in the title.

I read other posts where they said that the returned thing is not an array but an object. I checked it and I receive a proper array. Here it is (sorry for the external link): https://gyazo.com/e41ba0d77765cac5b871a0c80c99073a

What am I doing wrong here? I used type 'any' everywhere to make things easier for the time being. I gotta add that I first used this logic in my component (the merging logic) and with an 'async as' pipe I could solve the problem (it did not work without the 'as ...' part, I got the same error.

My service code:

    export class CryptoItemsService implements OnInit {
      allCryptoInformationMerged$!: any;
      constructor(private http: HttpClient) {}
    
      ngOnInit(): void {}
      // 1st HTTP request
      fetchAllCryptos() {...}
      // 2st HTTP request
      fetchMetadata() {...}
    
      mergeFetchedObjects() {
        this.allCryptoInformationMerged$ = forkJoin(
          this.fetchAllCryptos(),
          this.fetchMetadata()
        )
          .pipe(
            map(([prices, details]) => {
              const mergedObjects: any = [];
              for (const item of prices) {
                const currentDetailObject = details.find(
                  (detailObject: any) => detailObject.id === item.id
                );
                mergedObjects.push({
                  ...item,
                  ...currentDetailObject,
                });
              }
              return mergedObjects;
            })
          )
          .subscribe((data) => {
            this.allCryptoInformationMerged$ = data;
            console.log(this.allCryptoInformationMerged$);
          });
      }
    
      getallCryptos() {
        this.mergeFetchedObjects();
        return this.allCryptoInformationMerged$;
      }

My component file code:

    export class CryptoListitemsComponent implements OnInit {      
      cryptoItemGeneralDetails$!: any;
    
      constructor(private cryptoItemsService: CryptoItemsService) {}
    
      getCryptoData() {
        this.cryptoItemGeneralDetails$ = this.cryptoItemsService.getallCryptos();
      }
    }

My template code:

    <app-crypto-item
          *ngFor="let item of cryptoItemGeneralDetails$; index as i; even as e"
          [cryptoItem]="item"
          [index]="i"
          [even]="e"
    ></app-crypto-item>

CodePudding user response:

any is actually making things harder for you, the typescript compiler would've told you a few things you did wrong otherwise.

For example you're assigning allCryptoInformationMerged$ two completely different data types.

Here you're assigning it a Subscription.

this.allCryptoInformationMerged$ = forkJoin(...).pipe(...).subscribe(...);

Here you're assigning it the result of the Observable, I'm assuming it's an array.

.subscribe((data) => {
   this.allCryptoInformationMerged$ = data;
});

Note that calling subscribe does not immediately execute the callback function (data)=>{this.allCryptoInformationMerged$ = data;}. This function is only executed after your http requests emit results.

Then you return this property and use it in ngFor, which throws an error because it is a Subscription at first, which is not iterable.

getallCryptos() {
    this.mergeFetchedObjects(); // Sets property to Subscription
    return this.allCryptoInformationMerged$; // Returns before property is set to array
}

any basically says "Leave me alone I know what I'm doing". In my opinion it's especially important to stay away from it if you're using data types / functions you're not familiar with, because you don't know what you're doing yet (no offense). Typescript is there to help you as a developer, so you're only shooting yourself in the foot by not taking advantage of it.


Here's what a working example should look like

Service

  // I use any[] here because I don't know what your array contains
  // But you should replace it with the actual data schema
  getallCryptos(): Observable<any[]> {
    // This is just your mergeFetchedObjects method without the subscribe bit
    // I changed the forkJoin parameter to an array of Observables 
    // since the version you were using is deprecated
    // forkJoin will also take the type of array returned
    // I used [any, any] because I know it is length 2 but I don't know the types 
    return forkJoin<[any, any]>([this.fetchAllCryptos(), this.fetchMetadata()]).pipe(
      map(([prices, details]) => {
        const mergedObjects: any = [];
        for (const item of prices) {
          const currentDetailObject = details.find(
            (detailObject: any) => detailObject.id === item.id
          );
          mergedObjects.push({
            ...item,
            ...currentDetailObject,
          });
        }
        return mergedObjects;
      })
    );
  }

Component TS

export class CryptoListitemsComponent {
  cryptoItemGeneralDetails$ = this.cryptoItemsService.getAllCryptos();

  constructor(private cryptoItemsService: CryptoItemsService) {}
}

Component HTML

    <app-crypto-item
          *ngFor="let item of cryptoItemGeneralDetails$ | async; index as i; even as e"
          [cryptoItem]="item"
          [index]="i"
          [even]="e"
    ></app-crypto-item>

Notice the async pipe in HTML is what actually subscribes to the observable: https://angular.io/api/common/AsyncPipe

Generally you want to wait until the last possible moment to subscribe. Otherwise you're juggling local variables and trying to pass them around, as you experienced.

  • Related