ngx-virtual-scroller
ngx-virtual-scroller copied to clipboard
Loading in chunks, vsEnd keeps triggering with endIndex === buffer.length-1
What am I doing wrong? Without scrolling at all, vsEnd triggers everytime pics$ is updated, and with an endIndex value equaling the length of items-1. Should I not use AsyncPipe? Does virtual-scroller need to have a fixed pixel height instead of 100% (.h-100)?
@Component({
selector: 'app-pic-list',
templateUrl: './pic-list.component.html',
styleUrls: ['./pic-list.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PicListComponent implements OnInit, OnDestroy {
private take = 100;
destroy$ = new Subject();
pics$: Observable<IPic[]>;
private buffer: IPic[] = [];
private loading = false;
private gotAll$ = new Subject();
@ViewChild('scroll', { static: true }) scroll: VirtualScrollerComponent;
constructor(private api: ApiService) {}
ngOnInit() {
this.pics$ = this.scroll.vsEnd.pipe(
takeUntil(this.destroy$),
takeUntil(this.gotAll$),
startWith(null),
filter(
event =>
!this.loading && (!event || event.endIndex >= this.buffer.length - 1)
),
tap(e => console.log(e, this.buffer.length)),
tap(() => (this.loading = true)),
switchMap(() =>
this.api
.get<IPic[]>('pics', {
take: this.take,
skip: this.buffer.length
})
.pipe(
tap(pics => pics.length < this.take && this.gotAll$.next()),
tap(pics => (this.buffer = this.buffer.concat(pics))),
map(() => this.buffer),
tap(() => (this.loading = false))
)
)
);
}
ngOnDestroy() {
this.destroy$.next();
}
}
<virtual-scroller #scroll [items]="pics$ | async" class="h-100">
<app-pic [pic]="pic" *ngFor="let pic of scroll.viewPortItems"></app-pic>
</virtual-scroller>
// app-pic.scss
:host { display:inline-block; height:200px; width:200px; }
// Real scroll position should be close to 0
// tap(e => console.log(e, this.buffer.length))
$event: {
endIndex: 699
endIndexWithBuffer: 699
maxScrollPosition: 0
scrollEndPosition: 28000
scrollStartPosition: 0
startIndex: 0
startIndexWithBuffer: 0
}
this.buffer.length: 700
Tried also :host { display:block; height:200px } for app-pic, produced the same eternal vsEnd-loop so it's not due to being multi column.
Ok so indeed this seems to be caused by using any percentage height instead of pixels (height:50% etc, still same). <virtual-scroller #scroll [items]="pics$ | async" style="height:500px"> fixes it.
So how to fill the container height?
Ugly ass work-around below. Any better way?
hostHeight$: Observable<number>;
constructor(private api: ApiService, private host: ElementRef) {}
ngOnInit() {
this.hostHeight$ = fromEvent(window, 'resize').pipe(
startWith(null),
debounceTime(300),
map(() => this.host.nativeElement.offsetHeight),
tap(console.log)
);
<virtual-scroller
#scroll
[items]="pics$ | async"
[style.height]="(hostHeight$ | async) + 'px'"
>
Please convert your code to a StackBlitz if you'd like help with troubleshooting.
Weird, I can't reproduce it in Stackblitz which uses Angular 7, I'm on 8 with Ivy. Maybe it's got something to do with Ivy (and it not being prod rdy yet). :D Every vsEnd event just has the endIndex set to the last item and scrollEndPosition set to the highest possible value.
Sorry. I'll keep an eye on it later when Ivy becomes standard. For now, doesn't seem like anything to waste more time on.
Blitz anyway in case you wanna test with Ivy. All should be identical to the way I'm doing it in my actual app: https://stackblitz.com/edit/angular-1sw1l2
No wait, it was the Grid layout that was missing, that broke it. Updated Blitz. https://stackblitz.com/edit/angular-1sw1l2
Conclusion is if virtual-scroller is inside an 1fr grid row it doesn't matter if the component itself is display:block or flex, if it has a 100% height instead of a fixed pixel height the vsEnd event gets the wrong scroll position ie. thinks it's always scrolled to the bottom.
Setting the grid row height to a pixel amount also does fix this, eg. if you do this in the Blitz: grid-template-rows: auto 500px; instead of grid-template-rows: auto 1fr;
Interesting. Thanks for creating the stackblitz. I can reproduce the issue now.
I'm not sure off hand why it's broken, My first guess is that the grid layout is somehow causing the code to get incorrect measurements, but I'll need to do some debugging/troubleshooting.
I'm glad you found a workaround for now.
I too have the same issue. I put cards in the tab container. It is rendering properly. but it auto loads the remaining items as well without scroll interaction. Please any one suggest a workaround for this.
I too have the same issue. I put cards in the tab container. It is rendering properly. but it auto loads the remaining items as well without scroll interaction. Please any one suggest a workaround for this.
This works fine assuming your host component is taking up all the available viewport height (eg. flex): https://github.com/rintoj/ngx-virtual-scroller/issues/336#issuecomment-497924042
I'm using css grid as it works very well for masonry layout. The workaround to this problem for me was to use distinctUntilKeyChanged('scrollEndPosition') when subscribing to the vsEnd event.
I'm using css grid as it works very well for masonry layout. The workaround to this problem for me was to use
distinctUntilKeyChanged('scrollEndPosition')when subscribing to the vsEnd event.
But, the vsEnd event still fires in an infinite loop correct? You are just ignoring it?
Not infinite but a hell of a lot more than it should. It just fires a whole load of duplicate events. Last time I checked it fired around 7 or 8 times I think.
On Tue, Oct 5, 2021 at 12:56 AM Ray Suelzer @.***> wrote:
I'm using css grid as it works very well for masonry layout. The workaround to this problem for me was to use distinctUntilKeyChanged('scrollEndPosition') when subscribing to the vsEnd event.
But, the vsEnd event still fires in an infinite loop correct? You are just ignoring it?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/rintoj/ngx-virtual-scroller/issues/336#issuecomment-933944152, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABP4IEDAYMCJHYZCGW3EL3UFI5LVANCNFSM4HR76EKA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Yeah, it fires infinitely for me when you reach the end. It seems to have to do with an improper or failure to round fractional values. Forgetting the exact issue, but I'm pretty sure that someplace it is comparing the current scroll position (rounded) with the scroll position not rounded. So if you end up having a difference there then it will loop infinitely.