Ion-infinite-scroll not firing ionInfinite if records do not fill the page

ion-infinite-scroll does not fire the ionInfinite event if my initial retrieval of records does not fill the page.

I retrieve a batch of 10 records at a time and if I’m running the app in a full-screen browser this initial batch of records only fills about half of the infinite scroll area. I was expecting ionInfinite to be called to retrieve a second batch of records to fill the area (or be told that there are no more records).

If I shrink the window so that the area shows scrollbars then ionInfinite is called when the user scrolls down.

The user should not have to resize the window to get a full page of records.

There is an open issue for this from Ionic v3 Issue #59.

No solution, only workarounds that dig into the internals of the control which I’m loath to do given we are now at Ionic v5.

Is this issue being worked on?

Does anyone have a working workaround for v5?

Thanks.
Glenn.

Bump.

Others must have hit this problem.

I can increase the number of records I retrieve per page to fill all form factors, but I’m using Firebase and this would be an increase in costs to work around a bug in Ionic.

Hi Avaxa,

I did have the same issue and created the following work-around.
It seems to work correctly, but I still have the feeling that I am missing something obvious. I think this should be working out of the box.

However, I hope it might be useful to you.

regards, Theo

export class MyInfiniteListComponent<T> {
  data: T[] = [];
  isLoading = false;
  nextPage: number;
  private subscription: Subscription;

  protected service: MyDataService<T>;
  @ViewChild(IonContent, {static: false}) private content: IonContent;
  @ViewChild(IonInfiniteScroll, {static: false}) infiniteScroll: IonInfiniteScroll;

  @HostListener('window:resize', ['$event'])
  onResize() {
    this.fillPage();
  }

  resetView() {
    this.isLoading = false;
    this.data = [];
    this.nextPage = 0;
    this.infiniteScroll.disabled = false;
    this.loadData(true);
  }

  loadData(initial=false) {
    console.log('loadData:');
    if (this.isLoading) {
      console.log('still loading');
      this.infiniteScroll.complete();
      return;
    }

    const state = {} as ListStateModel;
    state.pageNumber = this.nextPage;
    state.pageSize = 10;
    this.nextPage += 1;
    this.isLoading = true;
    this.service.list(state).subscribe( result => {
      this.isLoading = false;
      this.data = [
          ...this.data,
          ...result.items,
      ];

      if (result.count === 0) {
        this.infiniteScroll.disabled = true;
      } else {
        if (initial) {
          this.fillPage();
        }
      }
      this.infiniteScroll.complete();
    });
  }

  fillPage() {
    this.checkForScrollbar().then(res => {
      if (!res) {
        console.log('no scrollbar, trying to load more data');
        this.loadData(true);
      }
    });
  }

  ionViewWillEnter() {
    this.resetView();
  }

  doRefresh(event) {
    this.resetView();
    event.target.complete();
  }

  async checkForScrollbar() {
    const scrollElement = await this.content.getScrollElement();
    return scrollElement.scrollHeight > scrollElement.clientHeight;
  }
}

Thanks Theos. I’ll try your workaround and appreciate you taking the time to reply.

I currently set my pageSize to something large enough to get a full page of records. It works but retrieves more data than strictly necessary.

Well after the fact but for future travelers: in my case, I was able to get this to work by simply setting the threshold to “0”.

when do you change the threshold to 0?

Technically it’s just this: <IonInfiniteScroll threshold="0">

In the end I found that this didn’t work, though. At least not reliably. What I settled on was a component that can force the scroll until results appear.

Here’s the Vue component I made to accomplish that, it’s not intelligible on its own because it imports some utility functions but it might have some insight in it.

<template>
  <IonInfiniteScroll threshold="0" @ionInfinite="doScroll" v-if="exists">
    <IonInfiniteScrollContent
      loadingSpinner="bubbles"
      loadingText="Loading…" />
  </IonInfiniteScroll>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref, onUpdated, onBeforeUnmount} from 'vue'
import {
  IonInfiniteScroll,
  IonInfiniteScrollContent
} from '@ionic/vue'
import { refAssign, requiredPrimitive, requiredType, wrapAsyncInInfiniteScroll } from '@/logic/patterns/vue'
import { notNullOrUndefined } from 'big-m/dist/types/utils'
import { isInScroll } from '@/dom/scroll'
import { combine } from '@/logic/patterns/functions'
import { ms } from '@/logic/patterns/async'

const KEEP_SCROLLING_INTERVAL_MS = 100

export default defineComponent({
  components: {
    IonInfiniteScroll,
    IonInfiniteScrollContent
  },
  props: {
    exists: Boolean,
    scroll: requiredType<() => Promise<any>>(Function),
    resultsListed: requiredPrimitive(Number)
  },
  setup(props) {
    const isMounted = ref(false)

    const scrollUntilResultsAppear = async () => {
      const currentlyListedResults = props.resultsListed

      const condition = () => props.resultsListed === currentlyListedResults && props.exists && isMounted.value

      if (condition()) {
        await props.scroll()
      }

      while (condition()) {
        await ms(KEEP_SCROLLING_INTERVAL_MS)
        await props.scroll()
      }
    }

    const doScroll = wrapAsyncInInfiniteScroll(scrollUntilResultsAppear)

    const loadUntilScrollable = async () => {
      const mainPageElement = notNullOrUndefined(
        document.querySelector('ion-content')
      )

      while (!isInScroll(mainPageElement) && isMounted.value && props.exists) {
        await scrollUntilResultsAppear()
      }
    }

    onMounted(
      combine(
        refAssign(isMounted, true),
        scrollUntilResultsAppear,
        loadUntilScrollable
      )
    )

    onUpdated(
      loadUntilScrollable
    )

    onBeforeUnmount(
      refAssign(isMounted, false)
    )

    return {
      doScroll
    }
  }
})
</script>
<style>
.infinite-loading {
  background: rgba(255,255,255,.5);
  padding: 1rem;
  margin-bottom: 0;
}

.infinite-scroll-content-md .infinite-loading-text {
  color: #333
}
</style>

I know this is old, but I encountered the same issue and found this thread. I couldn’t get ion-infinite-scroll to work properly either so I dumped it completely and built my own logic using IntersectionObserver

Stackblitz demo: IntersectionObserver Infinite Scroll - StackBlitz