Adding Pagination with Infinite Scroll
At the moment, we just get a bunch of GIFs, display them, and that’s it. We are going to improve this a little now. Instead of just dumping everything we get from a single request, we are going to only display a set amount of GIFs per “page”. When the user scrolls to the bottom of the page, we will automatically load in more GIFs if they are available.
To do this, we will need to make some changes to our getGifs()
stream, and we are also going to make use of the ion-infinite-scroll
component that Ionic provides for us.
Adding Infinite Scroll
Infinite scroll is one of the coolest Ionic components. The basic idea is that we can drop this into our ion-content
area:
<ion-infinite-scroll
threshold="100px"
(ionInfinite)="loadMore()"
>
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="Fetching gifs..."
>
</ion-infinite-scroll-content>
</ion-infinite-scroll>
Now, when the user is 100px
from the bottom of the content area it will trigger the ionInfinite
event whilst also displaying something to indicate that a load is happening. As you will soon see, we can also pass the ionInfinite
event to our loading process so that when the load is complete we can tell the infinite scroll component to stop displaying the loading indicator.
Our implementation for this will involve using our gifs
data in the template again. So, to avoid creating multiple subscriptions with the async
pipe for the same data, we are going to switch to using the vm$
stream approach again.
Add the following stream as a class member to the
HomeComponent
:
vm$ = combineLatest([this.gifs$.pipe(startWith([]))]).pipe(
map(([gifs]) => ({
gifs,
}))
);
This might look a bit unnecessary and silly, because it is. Using combineLatest
is pointless here because we only have one stream at the moment, so we could just use gifs$
directly rather than creating a vm$
stream with combineLatest
. However, just as we have with the other applications, we will be adding additional streams into this vm$
stream soon, so we are just setting it up this way now.
NOTE: Notice that we pipe
the startWith
operator onto our gifs$
stream. That is because getGifs
will not immediately return a value - it needs to fetch data from an external API. Since combineLatest
will only emit once all of its input observables have emitted a value, this will cause us problems. We are about to wrap our entire template in an *ngIf
that will subscribe to this vm$
stream. If it hasn’t emitted yet, then our entire template will not be displayed until it does. By using startWith([])
our gifs$
stream will immediately emit an empty array as an initial value, and then once the data has loaded in from Reddit it will use the proper value. This way, our template can still display before the HTTP
request has finished.
Modify the
HomeComponent
template to use thevm$
stream:
<ng-container *ngIf="vm$ | async as vm">
<ion-header>
<ion-toolbar>
<ion-title> Home </ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<app-gif-list
*ngIf="vm.gifs"
[gifs]="vm.gifs"
(gifLoadStart)="setLoading($event)"
(gifLoadComplete)="setLoadingComplete($event)"
></app-gif-list>
</ion-content>
</ng-container>
Now we can move on to implementing the infinite scroll.
Add the infinite scroll component inside of the content area:
<app-gif-list
*ngIf="vm.gifs"
[gifs]="vm.gifs"
(gifLoadStart)="setLoading($event)"
(gifLoadComplete)="setLoadingComplete($event)"
></app-gif-list>
<ion-infinite-scroll
threshold="100px"
(ionInfinite)="loadMore($event, vm.gifs)"
>
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="Fetching gifs..."
>
</ion-infinite-scroll-content>
</ion-infinite-scroll>