2

I am trying to use Ionic's Infinite Scroll with paginated data from an api that gets added to a ngrx store, but I'm not sure I fully understand how to make it work.

I found this SO answer for help with Observables, but getting data from the store is a little different. I can't subscribe to the response from the api since it is handled by an Effect.

Here's my simplified code:

Template

<ion-content padding>
  <ion-list>
    <ion-item *ngFor="let item of (items | async).results">
      <h2>{{ item.name }}</h2>
    </ion-item>
  </ion-list>

  <ion-infinite-scroll (ionInfinite)="infiniteScrolling$.next($event)">
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>

Component

export class InventoryPage {
  items: Observable<Items>;
  infiniteScrolling$: Subject<any> = new Subject();

  constructor(
    public store: Store<AppState>,
    public itemsActions: ItemsActions
  ) {}

  ngOnInit() {
    this.items = this.store.select(appState => appState.items);
    this.store.dispatch(createAction('FETCH_ITEMS'));

    // What should this.infiniteScrolling$ do here since dispatching
    // actions does not return an observable?
  }
}

Store

const initialState = { results: [] };

export function itemsReducer(items: Items = initialState, action: Action): Items {
  switch (action.type) {
    case 'FETCH_ITEMS_SUCCESS':
      return { results: [...items.results, ...action.payload.results] };
    default:
      return items;
  }
}

Am I on the right track and if yes, where should I call infiniteScroll.complete() once the data is returned from the api?

emroussel
  • 1,077
  • 11
  • 19
  • If you need an `Observable`, use `Observable.of` to create one. `import 'rxjs/add/observable/of';`. You can also use `Observable.from` to convert an array into an stream of its items. I do not think you need one though.... – Aluan Haddad Jul 17 '17 at 05:01
  • I see. What should I create an Observable from though? – emroussel Jul 17 '17 at 23:20

2 Answers2

3

If you're using NgRx, the state of the loading spinner (whether it shows or is hidden) should be saved in your store. This is a bit tricky with Ionic's infinite scroll implementation, because the visibility of the spinner isn't tied to a variable than you can easily link to state. In their implementation, the spinner shows automatically when ionInfinite is emitted and you have to explicitly call complete on the event dispatched to hide it again.

It would be ideal if you could pass an input to the ion-infinite-scroll element to show or hide the loading spinner. Then you could save a loading variable in your store that captures the status of the request and pass this to the ion-infinite-scroll element. When ionInfinite is emitted, simply dispatch an action to fetch the next page of results. In your reducer, the request action should set loading to true (so the spinner shows) and the sucess action dispatched in your effects should set it back to false (so the spinner is hidden again).

Since Ionic doesn't do this out of the box, you could add a directive to achieve this behaviour:

import { Directive, HostListener, Input, OnChanges, SimpleChanges } from "@angular/core";

@Directive({
  selector: '[appInfiniteScroll]'
})
export class InfiniteScrollDirective implements OnChanges {

  @Input() loading: boolean;

  private event: CustomEvent;

  @HostListener('ionInfinite', ['$event'])
  public onIonInfinite(event: CustomEvent) {
    this.event = event;
  }

  public ngOnChanges(changes: SimpleChanges): void {
    if (changes.loading && !this.loading && this.event) {
      (this.event as any).complete();
      this.event = null;
    }

  }

}

Then add it to the ion-infinite-scroll element and pass your loading state variable to the loading input:

<ion-infinite-scroll 
  appInfiniteScroll
  [loading]="loading$ | async"
  (ionInfinite)="fetchNextPage()">
  <ion-infinite-scroll-content>
  </ion-infinite-scroll-content>
</ion-infinite-scroll>

I put together an example in a StackBlitz if it's helpful.

mstuartf
  • 61
  • 5
0

I have this:

<ion-infinite-scroll (ionInfinite)="doInfinite($event)">

doInfinite(event) {
  this.store.dispatch({type: SET_FILTER, payload});
  event.target.complete();
}
bvamos
  • 773
  • 7
  • 10