ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.12k stars 13.5k forks source link

VirtualScroll rendering issue after altering source array #11851

Closed spacepope closed 7 years ago

spacepope commented 7 years ago

Ionic version: (check one with "x") [ ] 1.x (For Ionic 1.x issues, please use https://github.com/driftyco/ionic-v1) [ ] 2.x [x] 3.x

I'm submitting a ... (check one with "x") [x] bug report [ ] feature request [ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/

Current behavior: The scrolling behavior is as expected until some elements are added to the source array. Afterwards rapidly scrolling the list results in visible flickering of items or even empty item slots, which are filled after a short timeout or sometimes only triggered by an additional small scrolling push. This seems to occur with or without using headerFn and/or virtualTrackBy.

(Loading the complete list content within a VirtualScroll without altering the source array results in smooth and solid scrolling without the mentioned issue..)

Expected behavior: Altering the source array (adding or removing items) should not result in VirtualScroll rendering issues; scrolling should be as smooth and solid as after initialization.

Steps to reproduce:

  1. Use VirtualScroll and intialize source array
  2. Rapidly scroll up and down to verify smoothness of scrolling
  3. Alter source array by adding more items (could be done by simple setTimeout or by using InfiniteScroll)
  4. Rapidly scroll up and down; you eventually see the described flickering issue.

Related code:

<ion-content>
  <ion-list no-lines [virtualScroll]="Measures" approxItemHeight="'50px'"
        approxHeaderHeight="'50px'" [headerFn]="myHeaderFn" [virtualTrackBy]="trackByFn">
    <ion-item-divider *virtualHeader="let header" class="header">
      <h2>{{ header }}</h2>
    </ion-item-divider>

    <ion-item *virtualItem="let measure">
      <ion-icon [name]="measure.Icon" item-left></ion-icon>
      {{ measure.Value }} {{ measure.Unit}}
      <ion-note item-right>
        {{ measure.Caption }}
      </ion-note>
    </ion-item>
  </ion-list>

  <ion-infinite-scroll (ionInfinite)="doInfinite($event)">
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>
  trackByFn(index, item) {
    if (!item) {
      return undefined;
    }
    return item.TrackID;
  }

  myHeaderFn(record, recordIndex, records) {
    if (!record || !record.FormattedTime) {
      return null;
    }

    // show header on first item and each new measure time
    if (recordIndex === 0 || (recordIndex > 0 && record.FormattedTime !== records[recordIndex-1].FormattedTime)) {
      return record.FormattedTime;
    }
    return null;
  }

  // method for infinite scroll
  doInfinite(infiniteScroll) {
    // load next measures to display
    this.dataProvider.loadMeasureHistory(OFFSET, LIMIT).then(measures => { 
        measures.forEach(m => {
          this.Measures.push(m);
        });

        // report end of loading process, so that list can hide spinner
        infiniteScroll.complete();
    }).catch(err => { 
      infiniteScroll.complete();
    });
  }

Ionic info: (run ionic info from a terminal/cmd prompt and paste output below):

global packages:

    @ionic/cli-utils : 1.2.0
    Cordova CLI      : 6.4.0
    Ionic CLI        : 3.2.0

local packages:

    @ionic/app-scripts              : 1.3.7
    @ionic/cli-plugin-cordova       : 1.2.1
    @ionic/cli-plugin-ionic-angular : 1.2.0
    Cordova Platforms               : android 6.1.2
    Ionic Framework                 : ionic-angular 3.3.0

System:

    Node       : v6.9.4
    OS         : Windows 10
    Xcode      : not installed
    ios-deploy : not installed
    ios-sim    : not installed
wmaurer commented 7 years ago

I am also experiencing similar issues, but I'm not using ion-infinite-scroll. In my case, I'm updating a single item in the list immutably (via @ngrx/store), and the array gets newly created (as it's also immutable).

I see no refresh/flicker using *ngFor. I also checked the behaviour with ionic-angular 2.2.0, and that worked flawlessly, so this problem has been introduced since then.

The Ionic documentation says:

Changing the dataset requires the entire virtual scroll to be reset, which is an expensive operation and should be avoided if possible

... however in my opionion the [virtualScroll] should work as well as *ngFor.

I believe I also see another error: when the bound array contains items, then the binding changes to an empty array (e.g. via a filter), the ion-list with [virtualScroll] continues to display the items of the previous array.

Until this has been fixed, I've been able to copy the ionic-angular 2.2.0 [virtualScroll] code (which mostly flawlessly works for my purposes), rename it and use it in my app.

EDIT: After playing for a while. I see that copying in the ionic-angular 2.2.0 [virtualScroll] component doesn't work well, it causes other strange issues, so I don't recommend it. Now I really need a fix to [virtualScroll] as our app testers are complaining about the terrible experience with the flickering of the items in the list.

jgw96 commented 7 years ago

Hello, thanks for opening an issue with us, we will look into this.

wmaurer commented 7 years ago

@jgw96 Is there an Ionic 3.3.0 plnkr or codepen that I could fork to demonstrate the problems I'm having with virtualScroll?

wmaurer commented 7 years ago

Okay, I now have a Ionic 3.3.0 plnkr that demonstrates this problem: http://plnkr.co/edit/wcsNrJ?p=preview And here's a repo with the same code: https://github.com/wmaurer/ionic3-virtualscroll

Click on any item in the [virtualScroll] and the content of the ion-item gets increased. You can see also that all the items get redrawn and the list flickers.

I hope this helps in being able to replicate the problem.

masimplo commented 7 years ago

This seems related to #11294. Situation was improved by #11297 but just for edits outside the viewable port and additions deletions after the viewable port (fast path update). The rest take the slow path which essentially destroys and recreates all rendered items, thus causing the flickering and other side effects like closing a ion-slide-item that was open etc.
@manucorporat had mentioned that there might be a solution for not destroying the elements when a slow path is taken.

jgw96 commented 7 years ago

Hello all! After further review I am going to close this issue as a duplicate of #11788

ionitron-bot[bot] commented 6 years ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.