Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Observable in Angular 2

Tags:

angular

rxjs

I am trying out Angular 2's http GET to retrieve a list of top articles on HackerNews and after which I will retrieve their respective details in a nested observable.

I encounter this error when I try to loop and display the data in my HTML.

Cannot find a differ supporting object '[object Object]'

Cannot find a differ supporting object '[object Object]'

Also, I am guessing there should be a better way to do this, any pointer?

getTopPost() {
    this.http.get('https://hacker-news.firebaseio.com/v0/topstories.json')
      .map(res => res.json())
      .subscribe(
        data => { 
                    data.map(function(postId){
                            let storyUrl = "https://hacker-news.firebaseio.com/v0/item/"+ postId +".json";
                            that.http.get(storyUrl)
                                .map(res => res.json())
                                .subscribe(data => that.hnData = data, 
                                           err => that.logError(err),
                                           () => console.log(that.hnData));

                        });

                },
        err => this.logError(err);
      );

  }

HTML

<ion-item *ngFor="#item of hnData">
        {{item.title}}
</ion-item> 
like image 210
SteD Avatar asked Jan 07 '16 09:01

SteD


2 Answers

I think you can rewrite it in a more Rx-ish way like this:

getTopPost() {
  return http.get('https://hacker-news.firebaseio.com/v0/topstories.json')
    .map(res => res.json())
    .mergeMap(list => Observable.fromArray(list))
    .mergeMap(postId => http.get("https://hacker-news.firebaseio.com/v0/item/"+ postId +".json"))
    .map(res => res.json())
}
like image 108
luisgabriel Avatar answered Nov 13 '22 01:11

luisgabriel


I don't think it's a good practice to nest observable xhr calls... but I'm not a expert about it and can't tell you why you got this exception (maybe about this that var..).

But I have a different approach to show you:

A first component <top-stories> load the id list and then generate others component <top-story> for each one:

@Component({
  selector: 'top-stories',
  providers: [],
  template: '
    <div>
      <h2>Hacker news top stories:</h2>
      <ul>
        <li top-story *ngFor="#story; #i = index of list | async" [num]="i+1" [id]="story"></li>
      </ul>
    </div>
  ',
  directives: [TopStory]
})
export class TopStories {
  list: Observable<Array<number>>;

  constructor(private http: Http) {
    this.list = this.http.get('https://hacker-news.firebaseio.com/v0/topstories.json')
    .map(res => res.json())
    .map(list => list.slice(0, 30));
  }
}

The component <top-story> load itself post details and show it:

@Component({
  selector: '[top-story]',
  providers: [],
  template: `
    <div>
      <a *ngIf="item" [href]="item?.url">{{ num + ': ' + item?.title }}</a>
      <span *ngIf="!item">loading...</span>
    </div>
  `,
  directives: []
})
export class TopStory implements OnInit, OnDestroy {
  @Input() num: Number;
  @Input() id: Number;

  sub: any;
  item: object;

  constructor(private http: Http) {}

  ngOnInit() {
    this.sub = this.http.get('https://hacker-news.firebaseio.com/v0/item/' + this.id + '.json')
    .map(res => res.json())
    .subscribe(item => this.item = item);
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

You can play with it in this plunker: http://plnkr.co/edit/BRMlyD?p=preview

like image 25
bertrandg Avatar answered Nov 12 '22 23:11

bertrandg