Open kyleabens opened 5 years ago
It's happening the same here. Same version. Did you find any workaround on it?
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
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
Is this issue resolved? or did you guys find any workaround?
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
is this resolved? i have this error in version 4.9.0
Re-open please. It's still happens in 4.9.0. @eakoriakin @edy-ap
Same problem here. Ionic-selectable 4.9.0 and @angular/core 12.2.13. Re-open please.
@cunha20 i think nobody cares =(
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.
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
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.
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
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);
}
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);
...
}
This happens exactly when endInfiniteScroll() is called after the async call
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?
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.
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.
Can you show us a video of the result?
I put 200 items per page, so the client will hardly scroll to the end.... however the problem when reaching infinity scroll, continues...
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')}} </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
);
});
}
In my case, I implemented a workaround, I removed the ‘ion-item-group’ part from ionic-selectable-modal.component.html because I don't need it.
<ion-list class="ion-no-margin" *ngIf="selectComponent._hasFilteredItems">
<ion-item-group *ngFor="let group of selectComponent._filteredGroups" class="ionic-selectable-group">
<ion-item-divider *ngIf="selectComponent._hasGroups"
[color]="selectComponent.groupColor ? selectComponent.groupColor : null">
<!-- Need span for for text ellipsis. -->
<span *ngIf="selectComponent.groupTemplate" [ngTemplateOutlet]="selectComponent.groupTemplate"
[ngTemplateOutletContext]="{ group: group }">
</span>
<!-- Need ion-label for text ellipsis. -->
<ion-label *ngIf="!selectComponent.groupTemplate">
{{group.text}}
</ion-label>
<div *ngIf="selectComponent.groupEndTemplate" slot="end">
<div [ngTemplateOutlet]="selectComponent.groupEndTemplate" [ngTemplateOutletContext]="{ group: group }">
</div>
</div>
</ion-item-divider>
<ion-item button="true" detail="false" *ngFor="let item of group.items" (click)="selectComponent._select(item)"
class="ionic-selectable-item" [ngClass]="{
'ionic-selectable-item-is-selected': selectComponent._isItemSelected(item),
'ionic-selectable-item-is-disabled': selectComponent._isItemDisabled(item)
}" [disabled]="selectComponent._isItemDisabled(item)">
<!-- Need span for text ellipsis. -->
<span *ngIf="selectComponent.itemTemplate" [ngTemplateOutlet]="selectComponent.itemTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<!-- Need ion-label for text ellipsis. -->
<ion-label *ngIf="!selectComponent.itemTemplate">
{{selectComponent._formatItem(item)}}
</ion-label>
<div *ngIf="selectComponent.itemEndTemplate" slot="end">
<div [ngTemplateOutlet]="selectComponent.itemEndTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</div>
</div>
<span *ngIf="selectComponent.itemIconTemplate" [ngTemplateOutlet]="selectComponent.itemIconTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<ion-icon *ngIf="!selectComponent.itemIconTemplate"
[name]="selectComponent._isItemSelected(item) ? 'checkmark-circle' : 'radio-button-off'"
[color]="selectComponent._isItemSelected(item) ? 'primary' : null" [slot]="selectComponent.itemIconSlot">
</ion-icon>
<ion-button *ngIf="selectComponent.canSaveItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._saveItem($event, item)">
<ion-icon slot="icon-only" ios="create" md="create-sharp"></ion-icon>
</ion-button>
<ion-button *ngIf="selectComponent.canDeleteItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._deleteItemClick($event, item)">
<ion-icon slot="icon-only" ios="trash" md="trash-sharp"></ion-icon>
</ion-button>
</ion-item>
</ion-item-group>
</ion-list>
As a result, it now looks like this:
<ion-list class="ion-no-margin" *ngIf="selectComponent._hasFilteredItems">
@if (selectComponent._hasGroups){
@for (group of selectComponent._filteredGroups; track group){
<ion-item-group class="ionic-selectable-group">
<ion-item-divider *ngIf="selectComponent._hasGroups"
[color]="selectComponent.groupColor ? selectComponent.groupColor : null">
<!-- Need span for for text ellipsis. -->
<span *ngIf="selectComponent.groupTemplate" [ngTemplateOutlet]="selectComponent.groupTemplate"
[ngTemplateOutletContext]="{ group: group }">
</span>
<!-- Need ion-label for text ellipsis. -->
<ion-label *ngIf="!selectComponent.groupTemplate">
{{group.text}}
</ion-label>
<div *ngIf="selectComponent.groupEndTemplate" slot="end">
<div [ngTemplateOutlet]="selectComponent.groupEndTemplate" [ngTemplateOutletContext]="{ group: group }">
</div>
</div>
</ion-item-divider>
@for (item of group.items; track item){
<ion-item button="true" detail="false" (click)="selectComponent._select(item)"
class="ionic-selectable-item" [ngClass]="{
'ionic-selectable-item-is-selected': selectComponent._isItemSelected(item),
'ionic-selectable-item-is-disabled': selectComponent._isItemDisabled(item)
}" [disabled]="selectComponent._isItemDisabled(item)">
<!-- Need span for text ellipsis. -->
<span *ngIf="selectComponent.itemTemplate" [ngTemplateOutlet]="selectComponent.itemTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<!-- Need ion-label for text ellipsis. -->
<ion-label *ngIf="!selectComponent.itemTemplate">
{{selectComponent._formatItem(item)}}
</ion-label>
<div *ngIf="selectComponent.itemEndTemplate" slot="end">
<div [ngTemplateOutlet]="selectComponent.itemEndTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</div>
</div>
<span *ngIf="selectComponent.itemIconTemplate" [ngTemplateOutlet]="selectComponent.itemIconTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<ion-icon *ngIf="!selectComponent.itemIconTemplate"
[name]="selectComponent._isItemSelected(item) ? 'checkmark-circle' : 'radio-button-off'"
[color]="selectComponent._isItemSelected(item) ? 'primary' : null" [slot]="selectComponent.itemIconSlot">
</ion-icon>
<ion-button *ngIf="selectComponent.canSaveItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._saveItem($event, item)">
<ion-icon slot="icon-only" ios="create" md="create-sharp"></ion-icon>
</ion-button>
<ion-button *ngIf="selectComponent.canDeleteItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._deleteItemClick($event, item)">
<ion-icon slot="icon-only" ios="trash" md="trash-sharp"></ion-icon>
</ion-button>
</ion-item>
}
</ion-item-group>
}
} @else {
@for (item of selectComponent.items; track item){
<ion-item button="true" detail="false" (click)="selectComponent._select(item)"
class="ionic-selectable-item" [ngClass]="{
'ionic-selectable-item-is-selected': selectComponent._isItemSelected(item),
'ionic-selectable-item-is-disabled': selectComponent._isItemDisabled(item)
}" [disabled]="selectComponent._isItemDisabled(item)">
<!-- Need span for text ellipsis. -->
<span *ngIf="selectComponent.itemTemplate" [ngTemplateOutlet]="selectComponent.itemTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<!-- Need ion-label for text ellipsis. -->
<ion-label *ngIf="!selectComponent.itemTemplate">
{{selectComponent._formatItem(item)}}
</ion-label>
<div *ngIf="selectComponent.itemEndTemplate" slot="end">
<div [ngTemplateOutlet]="selectComponent.itemEndTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</div>
</div>
<span *ngIf="selectComponent.itemIconTemplate" [ngTemplateOutlet]="selectComponent.itemIconTemplate"
[ngTemplateOutletContext]="{ item: item, isItemSelected: selectComponent._isItemSelected(item) }">
</span>
<ion-icon *ngIf="!selectComponent.itemIconTemplate"
[name]="selectComponent._isItemSelected(item) ? 'checkmark-circle' : 'radio-button-off'"
[color]="selectComponent._isItemSelected(item) ? 'primary' : null" [slot]="selectComponent.itemIconSlot">
</ion-icon>
<ion-button *ngIf="selectComponent.canSaveItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._saveItem($event, item)">
<ion-icon slot="icon-only" ios="create" md="create-sharp"></ion-icon>
</ion-button>
<ion-button *ngIf="selectComponent.canDeleteItem" class="ionic-selectable-item-button" slot="end" fill="outline"
(click)="selectComponent._deleteItemClick($event, item)">
<ion-icon slot="icon-only" ios="trash" md="trash-sharp"></ion-icon>
</ion-button>
</ion-item>
}
}
</ion-list>
I am developing in Ionic 8 and Angular 17, which is why I switched from *ngFor
and *ngIf
to @for
and @if
.
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.