Display a list of messages and scroll to the bottom when a new message is received, even when I am at the top. I would like to scroll fully bottom even with elements of different heights.
With virtual scroll, I have to set the [itemSize]
property, but for me it is not a static value:
Also, I am using ng-content
to insert a button from the parent to load previous messages. What I see is that, when _scrollToBottom
is invoked, instead of taking me to the bottom, it takes me to a bit higher. I suspect this is because of the different heights of elements inside virtual-scroll.
I have read this autosize scroll strategy issue from Angular: https://github.com/angular/components/issues/10113; but I am not sure this will solve my problem.
Any idea of what I could do will be welcome.
Codesandbox: https://codesandbox.io/s/angular-virtual-scroll-biwn6
Video with the error: https://gofile.io/d/8NG9HD
The solution given by Gourav Garg works. Simply by executing twice the scroll function.
I am doing this now:
private _scrollToBottom() {
setTimeout(() => {
this.virtualScrollViewport.scrollTo({
bottom: 0,
behavior: 'auto',
});
}, 0);
setTimeout(() => {
this.virtualScrollViewport.scrollTo({
bottom: 0,
behavior: 'auto',
});
}, 50);
}
I think it is not very elegant but works fine.
This feature is added to CDK (Component Development Kit). Virtual scrolling shows up the visible dom elements to the user, as the user scrolls, the next list is displayed. This gives faster experience as the full list is not loaded at one go and only loaded as per the visibility on the screen.
cdkScrollable and ScrollDispatcher The cdkScrollable directive and the ScrollDispatcher service together allow components to react to scrolling in any of its ancestor scrolling containers. The cdkScrollable directive should be applied to any element that acts as a scrolling container.
[itemSize] dictates how tall in pixels each row in the list is. The virtual scroller then uses this (in part) to determine how many rows it can buffer above and below the viewport. The less tall you make the itemSize , the more it will try to load and buffer.
You can use if you are using cdk > 7
this.virtualScrollViewport.scrollToIndex(messages.length-1);
As this will move to the top of last item. You need to call scrollIntoView for that item.
this.virtualScrollViewport.scrollToIndex(this.numbers.length - 1);
setTimeout(() => {
const items = document.getElementsByClassName("list-item");
items[items.length - 1].scrollIntoView();
}, 10);
<cdk-virtual-scroll-viewport #virtualScroll style="height: 500px" itemSize="90">
<ng-container *cdkVirtualFor="let n of numbers">
<li class="list-item"> {{n}} </li>
</ng-container>
</cdk-virtual-scroll-viewport>
I have updated your sandbox
There's an alternative to cdk scroll.
Here's a genric version of code to scroll to a given HTML element. It can be used as a service function in angular or as a function in javascript.
scroll(el: HTMLElement, behaviour: any = "smooth", block: any = "start", inline: any = "nearest") {
el.scrollIntoView({ behavior: behaviour, block: block, inline: inline })
}
Sample HTML:
<div class="scroll-to-top" [ngClass]="{'show-scrollTop': windowScrolled}">
Back to top<button mat-button mat-icon-button (click)="scrollToTop()">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
</div>
Sample Typescript component code:
@HostListener("window:scroll")
onWindowScroll() {
if (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop > 100) {
this.windowScrolled = true;
}
else if (this.windowScrolled && window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop < 10) {
this.windowScrolled = false;
}
}
scrollToTop() {
(function smoothscroll() {
var currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
if (currentScroll > 0) {
window.requestAnimationFrame(smoothscroll);
window.scrollTo(0, currentScroll - (currentScroll / 8));
}
})();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With