Halaster
Halaster

Reputation: 1502

*ngFor Cannot read property 'subscribe' of undefined when given an Observable of type array

This is pretty straight forward, so I'm not sure why it's not working.

I have a service giving me an observable. I have a couple of rxjs streams which contribute to it like so:

search(searchTerm: string {

  this.searchStream = this.http.get("${this.baseUrl}searchq=${searchTerm}");

  this.resultStream = this.searchStream.map(
    data => {
      let obj = data.json();
      return {
        artists: obj.artists.items,
        albums: obj.albums.items,
        tracks: obj.tracks.items
      };
    },
    err => console.error(err),
    () => console.log('getRepos completed')
  );

  this.albumSearchStream = this.resultStream.map(
    searchResults => {
      console.log("mapping albums");
      return searchResults.albums
    },
    err => console.log("unable to get albums")
  );
  this.albumSearchStream.subscribe(
    albums => console.log(albums)
  );
}

Long story short, after a request the albumSearchStream gets an array of type EnvAlbum.

This is all happening within a service on Search.

This service is being injected into my Page controller, and in the constructor I am doing

this.albumResults = this.spotifyService.albumSearchStream;

where albumResults is going to be Observable of type EnvAlbum[].

Then in the template I am using a basic ngFor on this observable like so:

<ion-card *ngFor="let result of albumResults | async" >
        <ion-card-header>
            {{ result }}
        </ion-card-header>
    </ion-card>

However, then I get the following error: enter image description here

I am beyond stumped as to why this error is occurring.

NB: On subscribe, it is correctly outputting an array of objects

Upvotes: 3

Views: 1916

Answers (1)

Thierry Templier
Thierry Templier

Reputation: 202316

I think the problem that albumResults isn't a property of your component (it will be undefined) at the beginning so the async pipe can't subscribe on it. It seems to be set when you click to trigger the search method...

You could try to wrap the ngFor within an ngIf with the desugared expression

<template [ngIf]="albumResults"> <------
  <ion-card *ngFor="let result of albumResults | async" >
    <ion-card-header>
      {{ result }}
    </ion-card-header>
  </ion-card>
</template>

or initialize the albumResults with a fake observable.

Edit

I would refactor your service code this way:

albumResults = new Subject();

search(searchTerm: string) {
  this.searchStream = this.http.get(...);

  this.resultStream = this.searchStream.map(
    data => {
      let obj = data/*.json()*/;
      return {
        artists: obj.artists.items,
        albums: obj.albums.items,
        tracks: obj.tracks.items
      };
    }
  );

  this.albumSearchStream = this.resultStream.map(
    searchResults => {
      return searchResults.albums
    }
  );
  this.albumSearchStream.subscribe(
    albums => {
      this.albumResults.next(albums);
    }
  );
}

and set the albumResults property in your component this way:

constructor(private spotifyService:SpotifyService) {
  this.albumResults = this.spotifyService.albumResults;
}

See this plunkr: https://plnkr.co/edit/IrsZ9P3wff2YXyjRRGTw?p=preview.

Upvotes: 2

Related Questions