Ionic-framework: bug: ion-infinite-scroll on top

Created on 3 Oct 2019  Â·  15Comments  Â·  Source: ionic-team/ionic-framework

Bug Report

Ionic version:
[x] 3.x
[x] 4.x

Current behavior:
Happen the same in V4
All info here -->
ionic-team/ionic-v3#831
Almost 1 year with that issue opened

Expected behavior:

Steps to reproduce:
HTML :

<ion-header>
  <ion-toolbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-toolbar>
 </ion-header>
 <ion-content >
  <ion-infinite-scroll [position]="top" (ionInfinite)="getMoreMessagesI($event)">
    <ion-infinite-scroll-content loadingSpinner="bubbles"></ion-infinite-scroll-content>
  </ion-infinite-scroll>
  <ion-list>
    <ion-item *ngFor="let message of messages; let i = index" attr.id="{{i}}">
      <p>{{ message.name }}</p>
    </ion-item>
  </ion-list>
 </ion-content>

TS

import { Component, OnInit, ViewChild } from '@angular/core';
import { IonContent, IonInfiniteScroll } from '@ionic/angular';
@Component({
 selector: 'app-home',
 templateUrl: 'home.page.html',
 styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
  @ViewChild(IonInfiniteScroll, { static: true }) infiniteScroll: IonInfiniteScroll;
 @ViewChild(IonContent, { static: true }) content: IonContent;

 private messages: any[] = [];
 private delay = (time: number) => new Promise(res => setTimeout(() => res(), time));
 constructor() {}
 async ngOnInit() {
   this.setInitialMessages();
 }
 private async getMoreMessagesI(event) {
   const more = [
     {name: Math.random()},
     {name: Math.random()},
     {name: Math.random()},
     {name: Math.random()},
     {name: Math.random()},
   ]; 
   await this.delay(2000);
   event.target.complete();
   this.messages.unshift(...more);
 }

 private setInitialMessages() {
   const initial = [
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
     {name: 'a'},
   ];
   this.messages.push(...initial);
 }
}

Ionic info:

Ionic:

   Ionic CLI                     : 5.0.1 (/Users/pc/Desktop/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.10.0
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.0.0

Utility:

   cordova-res : 0.6.0
   native-run  : 0.2.8

System:

   NodeJS : v10.16.3 (/Users/pc/.nvm/versions/node/v10.16.3/bin/node)
   npm    : 6.9.0
   OS     : macOS Mojave

DEMO
https://imgur.com/a/EsRy84Q

core bug

All 15 comments

Same bug for me (with ionic v4)!

For me also the same bug on Ionic v4. I have a messaging application, and this bug really breaks the user experience...

Happens to me in Ionic 5 too!

Yep, Ionic 5 still not fixed smh. Don't get me wrong I appreciate all the hard work and Ionic seems great so far but this issue has been around since v3 come on. @michielcrabbe96 what did you end up doing ? my chat app also needs this

Guess I'll just leave it at bottom, transform rotate 180deg and call it a day lol
EDIT: actually what I ended up doing is I enabled scroll events on , retrieved the underlying scrolling element by calling .getScrollElement() on IonContent's view child and in (ionScroll) callback figure out whether we are at bottom or top that way can infinite scroll both ways
`
public scrollHandler(e: any): void {

  const top = this.scrollingElement.scrollTop;
  const height = this.scrollingElement.scrollHeight;
  const offset = this.scrollingElement.offsetHeight;

  if(top > height - offset -1) {
    console.log("bottom");
  }

  if(top === 0 && !this.isLoadingOlderMessages) {
    console.log("top")
  }

}`

Guess I'll just leave it at bottom, transform rotate 180deg and call it a day lol
EDIT: actually what I ended up doing is I enabled scroll events on , retrieved the underlying scrolling element by calling .getScrollElement() on IonContent's view child and in (ionScroll) callback figure out whether we are at bottom or top that way can infinite scroll both ways
`
public scrollHandler(e: any): void {

  const top = this.scrollingElement.scrollTop;
  const height = this.scrollingElement.scrollHeight;
  const offset = this.scrollingElement.offsetHeight;

  if(top > height - offset -1) {
    console.log("bottom");
  }

  if(top === 0 && !this.isLoadingOlderMessages) {
    console.log("top")
  }

}`

I tried this code in my Ionic 5 app but it is still showing the same behavior. I have to scroll down a little bit and then scroll up to get the new data. The scrolling isn't smooth. Is there a way that I can detect the screen height ? and when user scrolls to that screen height, I can fetch the data.

  logScrolling(event) {
    const top = event.detail.scrollTop;
    const height = event.detail.scrollHeight;
    const offset = event.detail.offsetHeight;

    if (top > height - offset - 1) {
      console.log('bottom');
    }

    if (top === 0 && !this.resultEmpty) {
      console.log('top');
      this.getData();
    }
  }

Guess I'll just leave it at bottom, transform rotate 180deg and call it a day lol
EDIT: actually what I ended up doing is I enabled scroll events on , retrieved the underlying scrolling element by calling .getScrollElement() on IonContent's view child and in (ionScroll) callback figure out whether we are at bottom or top that way can infinite scroll both ways
`
public scrollHandler(e: any): void {

  const top = this.scrollingElement.scrollTop;
  const height = this.scrollingElement.scrollHeight;
  const offset = this.scrollingElement.offsetHeight;

  if(top > height - offset -1) {
    console.log("bottom");
  }

  if(top === 0 && !this.isLoadingOlderMessages) {
    console.log("top")
  }

}`

I tried this code in my Ionic 5 app but it is still showing the same behavior. I have to scroll down a little bit and then scroll up to get the new data. The scrolling isn't smooth. Is there a way that I can detect the screen height ? and when user scrolls to that screen height, I can fetch the data.

  logScrolling(event) {
    const top = event.detail.scrollTop;
    const height = event.detail.scrollHeight;
    const offset = event.detail.offsetHeight;

    if (top > height - offset - 1) {
      console.log('bottom');
    }

    if (top === 0 && !this.resultEmpty) {
      console.log('top');
      this.getData();
    }
  }

I haven't fine tuned it but looking at how iMessage works I realize that once you scroll up in message history and get to the point where older messages need to be loaded it does so but the the view stays where it was before loading as opposed to my app automatically scrolling to top once older messages are loaded which then would require like you said to scroll down a bit and then up again to initiate another load

Perhaps ios automatically scrolls to the top when new data is added to the list.

<ion-content>
  <ion-list>
    <button ion-item *ngFor="let item of items">
      {{ item }}
    </button>
  </ion-list>
  <button ion-button (click)="onClick()">test</button>
</ion-content>
// ...
  items = [];

  onClick() {
    this.items.unshift(...[1, 2, 3, 4, 5])
  }

When testing in the browser, data is accumulated at the top of the list, but does not scroll.

When testing on an ios device, the data is stacked at the top of the list and it works until scrolling.

A similar issue is on stackoverflow

I found how to fix it temporarily

// ...
loadMoreMessages(infiniteScroll: InfiniteScroll) {
  this.getMessage()
      .subscribe((data) => {
        // block scrolling
        this.content.getScrollElement().style.overflow = 'hidden';

        // something

        setTimeout(() => {
          // continue scrolling
          this.content.getScrollElement().style.overflow = 'scroll';
        }, 0);
      });
}

i have the same error in ionic 5.
On ios device when I add items to the list an unwanted scroll to top is triggered.
In debug it is noticed that in the moment in which the list changes of value the page is empty.

    // block scrolling
    this.content.getScrollElement().style.overflow = 'hidden';

How do you define this.content?

@emanuele-galeotti

I defined this.content as @ViewChild(IonContent) content

@heecheolman
with your work around i have this error
[Error] TypeError: undefined is not an object (evaluating 'this.content.getScrollElement().style.overflow = 'hidden'') — 11-es2015.9393e9903d18441957ee.js:612
capacitorConsole (user-script:2:78)
(funzione anonima) (user-script:2:356)
(funzione anonima) (user-script:2:376)
[Error] TypeError: undefined is not an object (evaluating 'this.content.getScrollElement().style.overflow = 'hidden'')
runTask (polyfills-es2015.af1b58a93b762e6d82c7.js:184)
invokeTask (polyfills-es2015.af1b58a93b762e6d82c7.js:493)
timer (polyfills-es2015.af1b58a93b762e6d82c7.js:2565)

@emanuele-galeotti

Could you try it in the ngAfterViewInit hook?

i think the problem is not a time reference based on the component life cycle, but the fact that getScrollElement is an asynchronous function that returns a promise.
and the .style of a promise does not exist

@emanuele-galeotti

In Ionic3, it was not a Promise.

Could you try with async/await on Ionic5?

async foo() {
  const scrollElement = await this.content.getScrollElement();
  // ...
}
Was this page helpful?
0 / 5 - 0 ratings