0

I have a remote collection of

interface Foo {
  id: string;
  properties: any;
}

Which I can access using a

class FooAPIService {
  find(skip = 0): Promise<Foo[]> {
    // promises at most 5 Foos. to get more, use skip > 1
    // pagination is server side
  }

  on(event: string, callback: (foo: Foo) => any) {
    // registers event handler, for events: created, updated, removed
    // events triggered when a change occurs server side
  }
}

As a newcomer to Angular2 (and Ionic2), I'm trying to figure out how to consume this according to best practices. I've been looking at examples of Observables, but I'm having trouble figuring out how to apply them correctly to this use case:

I'm producing a page with endless scroll, leveraging Ionic's InfiniteScroll component:

@Component({
  selector: 'page-foo-list',
  template: `
    <ion-list>
      <ion-item *ngFor="let foo of foos">{{foo}}</ion-item>
    </ion-list>

    <ion-infinite-scroll (ionInfinite)="doInfinite($event)">
      <ion-infinite-scroll-content></ion-infinite-scroll-content>
    </ion-infinite-scroll>
  `
})
class FooListPage {
  private foos;

  doInfinite(infiniteScroll) {
    // do something async to fetch more Foos,
    // then call infiniteScroll.complete()
  }
}

I know I can hook into the events using Observable.fromEvent(fooAPIService, 'created') (same for update & removed) and wrap the find using Observable.fromPromise(fooAPIService.find()) or Observable.fromPromise(fooAPIService.find(123)), but I can't figure out how to hook them up to produce something ngFor can consume.

Any help or pointers in the right direction are appreciated!

bosticko
  • 763
  • 9
  • 21
  • Why does your `find()` method return a Promise in the first place? You'd be better off returning an Observable from that method rather than wrapping the method call in a promise like you suggested. DON'T DO THIS: `Observable.fromPromise(fooAPIService.find())` – AngularChef Jan 24 '17 at 09:55
  • Once you have an Observable in your `find()`, you can use all the RxJS operators to convert the data into a consumer-friendly format **before** returning it. Does that make sense? – AngularChef Jan 24 '17 at 09:56

2 Answers2

3

First you need to add <ion-infinite-scroll> in between <ion-content>

As a Observable fashion you can implement it like this:

@Component({
  selector: 'page-foo-list',
  template: `
  <ion-content padding>
    <ion-list>
      <ion-item *ngFor="let foo of foos">{{foo}}</ion-item>
    </ion-list>

    <ion-infinite-scroll (ionInfinite)="infiniteScrolling$.next($event)">
      <ion-infinite-scroll-content></ion-infinite-scroll-content>
    </ion-infinite-scroll>
  </ion-content>
  `
})
class FooListPage implements OnInit, OnDestroy {
  private foos;
  private infiniteScrolling$: Subject<any> = new Subject();
  private fooApiService$: Observable<any>;
  private destroy$ : Subject<any> = new Subject();

  constructor(public fooAPIService : FooAPIService){}

  ngOnInit(){

    this.fooApiService$ = Observable
      .fromPromise(this.fooAPIService.find())
      .map(foos => this.foos = foos);

    this.infiniteScrolling$
      .takeUntil(this.destroy$)
      .delayWhen(() => this.fooApiService$)
      .map(infiniteScroll => infiniteScroll.complete())
      .subscribe();

  }

  ngOnDestroy(){
    this.destroy$.next();
    this.destroy$.unsubscribe();
  }

}
Victor Godoy
  • 1,642
  • 15
  • 18
0

As you are using Promise to find Foo you can use then

  doInfinite(infiniteScroll) {
    this.fooAPISerivice.find().then((data)=>{
      this.foos.push(data);
    // do something async to fetch more Foos,
    // then call infiniteScroll.complete()
    }
  }
raj
  • 5,989
  • 7
  • 30
  • 62
  • 1
    I went down this road, but it seems to leave me manually managing the contents of an array, from this callback, and 3 others for hooking into the `created`, `updated`, & `removed` events, which feels... Wrong? It seems in contrast to what I see around Angular2, and I get the impression I'm missing a pattern for doing this better. Perhaps I'm mistaken. – bosticko Jan 24 '17 at 08:35