ionic-selectable icon indicating copy to clipboard operation
ionic-selectable copied to clipboard

4.4.0 infiniteScroll flickers and resets scroll position

Open kyleabens opened this issue 6 years ago • 21 comments

I just dowloaded v4.4.0 and implemented infinite scroll and noticed that it flickers when I add new data to event.components.items. Doesn't matter if I push or concat the data. I also noticed that every time onInfiniteScroll fires it resets the scroll position to the very top which is probably connected to the flicker in some way. You can see what I'm talking about below.

ion-selectable-infinitescroll

kyleabens avatar Apr 02 '19 05:04 kyleabens

It's happening the same here. Same version. Did you find any workaround on it?

gabriel17carmo avatar May 18 '19 23:05 gabriel17carmo

I'm facing the same Issue in my case the Screen is blinking each time I Increment the array of items, the fact of the scroll backs to the TOP only happens if Focus in other INPUT, I don't know if it's relevant, but I'm using ReactiveForms

VERSION: ^4.4.1 selectable

MyCode TS:

  async getIcones() {
    this.iconeSub = this.baseService.fillSelect('icones', this.iconesPage).subscribe((icones: any[]) => {
      this.icones = this.icones.concat(icones);
    }, (e) => {
      this.utils.showError(e);
    });
  }
  async getMaisIcones(event: {
    component: IonicSelectableComponent,
  }) {
    this.iconesPage++;
    await this.getIcones();
    event.component.endInfiniteScroll();
  }

MyCode HTML:

<ionic-selectable slot="end" 
              formControlName="icone" 
              [items]="fill.icones" 
              itemValueField="id"
              itemTextField="nome_icone"
              closeButtonText="Fechar" 
              [hasInfiniteScroll]="true" 
              (onInfiniteScroll)="fill.getMaisIcones($event)" 
              (onClose)="fill.iconeSub.unsubscribe(); 
              fill.icones = []; 
              fill.iconesPage = 0">
              <ng-template ionicSelectableItemEndTemplate let-icone="item">
                <ion-icon [name]="icone.nome_icone"></ion-icon>
              </ng-template>
            </ionic-selectable>

I'm using a shared service, and the itens is there, I tryed to use event.component.concat(bla), and the same happens

tiagosilveiraa avatar Aug 15 '19 02:08 tiagosilveiraa

Is this issue resolved? or did you guys find any workaround?

abhideshmukh avatar Mar 26 '20 12:03 abhideshmukh

Error persists in version "ionic-selectable": "4.7.1",

One thing that night was: When I run in debug mode it does not return to the beginning, it works as expected. But when I generate the build the problem happens

vagnersabadi avatar Feb 02 '21 15:02 vagnersabadi

is this resolved? i have this error in version 4.9.0

clonixdev avatar Jun 10 '21 01:06 clonixdev

Re-open please. It's still happens in 4.9.0. @eakoriakin @edy-ap

4-life avatar Aug 07 '21 08:08 4-life

Same problem here. Ionic-selectable 4.9.0 and @angular/core 12.2.13. Re-open please.

https://user-images.githubusercontent.com/33962782/141646665-1ea9aa7d-cb28-44b5-ae2c-5643ce827794.mp4

cunha20 avatar Nov 13 '21 14:11 cunha20

@cunha20 i think nobody cares =(

4-life avatar Nov 15 '21 09:11 4-life

Guys, this is an opensource project, @edy-ap and myself have put lots of efforts into it getting nothing back. If you find an issue, I'd recommend you clone the project, deploy it locally and try to fix it. Then you can submit a PR.

eakoriakin avatar Nov 15 '21 09:11 eakoriakin

Guys, this is an opensource project, @edy-ap and myself have put lots of efforts into it getting nothing back. If you find an issue, I'd recommend you clone the project, deploy it locally and try to fix it. Then you can submit a PR.

we know but we just say that you should re-open the issue because bug is still exists. Maybe somebody will find a solution for this

@eakoriakin

4-life avatar Nov 15 '21 09:11 4-life

Hi guys, I analyzed the project, but I couldn't find the root cause of the problem. To move forward, I've implemented the function below for non-iOS devices. It's not the best solution, but it's working.

image

cunha20 avatar Dec 04 '21 15:12 cunha20

Hey there, anyone got a solution or idea for this? Facing this issue and hoping for an answer.

Cheers!

Update

I just applied a custom workaround now, which is kind of based on the aproach of @cunha20. Thanks for this input.

It is far away from being optimized, but i just paste it here for anyone who is interested. Feel free to ping me, if you have further adjustments or ideas.

care

  • not tested in emulators (IOS|Android)
  • not tested on real devices (IOS|Android)

Description I do listen to the change of the ion-list element height and apply the solution @canha20 posted here. So instead of adding a timeout, we got the observer. Also i do save the current scroll position and get back to it once the height got updated. So there is no fixed height numbers.


observer: ResizeObserver;
  scrollHeight : number = 0;
  scrollTopPosition: number = 0;
  modalScrollElement: HTMLElement

onCloseSelect() {
    this.observer.unobserve(this.modalScrollElement);
  }

/**
   * We do lose the observer here, because the element is resetted.
   * STILL: the scroll jump issue does not appear after a search.
   * MAYBE: We could trigger the method tryRegisterObserver() once a search is completed.
   * ADDITION: If the issue does not appear after a scroll, maybe this is also fixable by just calling a focus or inital emptysearch
   */
  searchEvent(event: { text: string }) {
    this.scrollHeight = 0;
    this.scrollTopPosition = 0;

    this.search(event)
  }

  onOpen() {
    setTimeout(() => {
      this.tryRegisterObserver();
    }, 1000);
  }

  /**
   * TODO: Better also check if
   * this.selectComponent._modalComponent._content exists, or retry
   * Somtimes the entries need more time to load, so using a timeout is not the best solution
   */
  tryRegisterObserver() {
    if(!this.selectComponent.isOpened) {
      setTimeout(() => {
        this.tryRegisterObserver()
      }, 1000);
    } else {
      this.registerModalObserver()
    }
  }

  registerModalObserver() {
    this.observer = new ResizeObserver(entries => {
      this.zone.run(() => {
        const newHeight = entries[0].contentRect.height;

        if(this.scrollHeight < newHeight) {
          this.scrollHeight = newHeight;
          this.selectComponent._modalComponent._content.scrollToPoint(0, this.scrollTopPosition)
        }
      });
    });

    /**
     * TODO: Check if element is accesable, otherwise try register observer once more
     * child is: ion-content -> ion-list
     */
    this.modalScrollElement = this.selectComponent._modalComponent._element.nativeElement.children[1].children[0]

    this.selectComponent._modalComponent._content.getScrollElement().then((element: HTMLElement) => {
      element.addEventListener("scroll", (event) => {
        if(this.scrollTopPosition < element.scrollTop) {
          this.scrollTopPosition = element.scrollTop
        }
      })
    })

    this.observer.observe(this.modalScrollElement);

  }

sfox-developer avatar Jun 08 '22 15:06 sfox-developer

Or simply move scroll to bottom in search method with: event.component._modalComponent._content.scrollToBottom(1500); (tested in ionic-selectable 4.9.0)

html:

    <ionic-selectable item-content [(ngModel)]="port" itemValueField="id"
      itemTextField="name" [items]="ports" [canSearch]="true"
      [isMultiple]="false" [hasInfiniteScroll]="true"
      (onSearch)="searchPorts($event)"
      (onInfiniteScroll)="getMorePorts($event)">
    </ionic-selectable>

ts:

searchPorts(event: { component: IonicSelectableComponent; text: string }) {
	
	event.component._modalComponent._content.scrollToBottom(1500);
	
	...
}

https://user-images.githubusercontent.com/14658236/201833440-ed81bf99-9f80-4737-9ede-8154035c886c.mp4

git-rlagos avatar Nov 15 '22 05:11 git-rlagos

This happens exactly when endInfiniteScroll() is called after the async call

neverlose-lv avatar Nov 15 '22 11:11 neverlose-lv

This happens exactly when endInfiniteScroll() is called after the async call

But do you suggest removing the endInfiniteScroll()? Or do I have to add some code before or after? Did you fix it?

git-rlagos avatar Nov 15 '22 12:11 git-rlagos

But do you suggest removing the endInfiniteScroll()? Or do I have to add some code before or after? Did you fix it?

I have tried to avoid it. And made more investigation on this problem. If you avoid the endInifniteScroll() - then new items do not appear after the very first infinite query, you have to scroll a bit upper and then to bottom, but the next ones - work ok. Also you can add the .complete() method to the internal ion-inifinite-scroll element.

The bug seems to be that the items does not appear - that there is no change detection. But once I add changedetection, the list behavior acts the same - it is fully redrawn, and scrolling starts from the top.

Then I investigated deeper and saw, that on infinite scroll, the items array is fully reassigned internally in the ionic-selecatable.

So the list is redrawn, that's why the scroll position is reset. It does not matter, how you set the component items, because when you call endInfiniteScroll(), it internally calls setItems() method, which copies the values you have set, to the internal array, by creating a new array. So there is no difference, how you set the items. concat/push/... etc. anyway a new array of items is created.

I think - you are not able to keep the scroll position, since the view is redrawn. (but this may be tricky, may be there is some ionic property for this?)

There should be the trackBy function for the list and groups to in the ionic-selectable to fix this issue.

Or there should be some ionic property, to keep the scroll on the same position, while the view is being redrawn.

neverlose-lv avatar Nov 15 '22 13:11 neverlose-lv

To put it into production, I increased the number of items to work around the problem. Below is the code snippet. I'm using ionic-selectable 4.9.0.

image

cunha20 avatar Nov 18 '22 12:11 cunha20

Can you show us a video of the result?

git-rlagos avatar Nov 18 '22 12:11 git-rlagos

I put 200 items per page, so the client will hardly scroll to the end.... however the problem when reaching infinity scroll, continues...

https://user-images.githubusercontent.com/33962782/202711861-1f979bf4-cf39-4508-9d86-fedba635f644.mp4

cunha20 avatar Nov 18 '22 13:11 cunha20

Ok, only size items. I' show you my code and result for scrolling. In file html, onInfiniteScroll have the method with $event for get IonicSelectableComponent in file TS.

html:

<ion-item *ngIf="!esMantencionPreventiva" [disabled]="!tarea?.initial_date">
        <ion-label position="floating">Tipo falla</ion-label>
        <ion-textarea *ngIf="tareaFirmada()" [value]="tarea?.types_description" auto-grow readonly></ion-textarea>
        <ionic-selectable [disabled]="!tarea?.initial_date" *ngIf="!tareaFirmada()"                       
                          closeButtonSlot="end" #tipoFallaComponent item-content
                          [(ngModel)]="tipoFalla" [items]="dataService.tipoFallas" 
                          itemValueField="description" itemTextField="description" 
                          [canSearch]="true" [canSaveItem]="true"
                          (onOpen)="onOpen()" (click)="onClick()"
                          [canClear]="true"                      
                          (onSearchFail)="onSearchFail('tipoFalla', $event)"
                          (onSearchSuccess)="onSearchSuccess($event)"
                          [hasInfiniteScroll]="true"
                          (onInfiniteScroll)="onInfiniteScroll('tipoFalla', $event)"
                          (onSearch)="searchType('tipoFalla', $event)"
                          (onChange)="onSelectedChange('tipoFalla', $event)">
          <ng-template ionicSelectableCloseButtonTemplate>
            <ion-text color="danger">{{paginaTipoFallo}}/{{getNumeroPaginas('tipoFalla')}} &nbsp; </ion-text>
            <ion-icon name="close-circle" style="font-size: 24px;"></ion-icon>
          </ng-template>
          <ng-template ionicSelectableItemTemplate let-port="item">
            {{port.description}}
          </ng-template>
          <ng-template ionicSelectableValueTemplate let-port="value">
            <div class="ion-text-wrap">{{port.description}}</div>
          </ng-template>
          <ng-template ionicSelectableAddItemTemplate let-port="item" let-isAdd="isAdd">
            <form [formGroup]="formTipoFalla" novalidate>
              <ion-list>
                <ion-item-divider>
                  {{isAdd ? 'Nuevo' : 'Editar'}} registro tipo de falla.
                </ion-item-divider>                
                <ion-item>
                  <ion-label color="tertiary">Título</ion-label>
                  <ion-textarea formControlName="description" 
                                auto-grow placeholder="Ingrese tipo..."
                                autocorrect="off" autocapitalize="none">
                  </ion-textarea>
                </ion-item>
                <ion-item color="danger" slot="error" *ngIf="formTipoFalla.get('description').hasError('tipoAlreadyExists')">
                  <ion-label>Ya existe el tipo.</ion-label>
                </ion-item>
              </ion-list>
            </form>
            <ion-footer>
              <ion-toolbar mode="ios">
                <ion-row>
                  <ion-col class="ion-text-center">
                    <ion-button ion-button full no-margin color="danger"
                                (click)="tipoFallaComponent.hideAddItemTemplate()">
                      Cancel
                    </ion-button>
                  </ion-col>
                  <ion-col class="ion-text-center">
                    <ion-button ion-button full no-margin 
                      (click)="isAdd? agregarTipo('tipoFalla') : editarTipo('tipoFalla', tipoFalla)"
                      [disabled]="!formTipoFalla.valid">
                      {{isAdd ? 'Agregar' : 'Editar'}}
                    </ion-button>
                  </ion-col>
                </ion-row>
              </ion-toolbar>
            </ion-footer>
          </ng-template>
        </ionic-selectable>
      </ion-item>

ts:

// Metodo para usar con Infinity Scroll en ionic-selectable
  onInfiniteScroll(tipo: string, 
                event: { component: IonicSelectableComponent; text: string }) {

    event.component._modalComponent._content.scrollToBottom(1500);  // <---- Fixed position

    const page = Math.round(event.component.items? (event.component.items.length / this.cantItemsPorPagina) : 1);
    const text = (event.text || '').trim().toLowerCase();

    switch(tipo){
      case 'tipoFalla': {
            this.paginaTipoFallo = page;
            if(this.paginaTipoFallo > (this.getNumeroPaginas(tipo))){
              event.component.disableInfiniteScroll();
              return;
            }

            this.dataService.getTipoFallaAsync(++this.paginaTipoFallo, this.cantItemsPorPagina)
            .subscribe((tipos) => {
              tipos = event.component.items.concat(tipos);
        
              if (text) {
                tipos = this.filterRecordsTipoFalla(tipos, text);
              }
        
              event.component.items = tipos;
              event.component.endInfiniteScroll();
            });
            break;
      }
     //.... more code case
    }    
  }

  filterRecordsTipoFalla(tipos: TipoFalla[], text: string): TipoFalla[] {
    return tipos.filter((tipo) => {
      return (
        tipo.description.toLowerCase().indexOf(text) !== -1 
      );
    });
  }

https://user-images.githubusercontent.com/14658236/202734562-f67c272b-57c5-49e7-90b9-fde878c7f61e.mp4

git-rlagos avatar Nov 18 '22 15:11 git-rlagos