Add support for automatically re-rendering the list when the viewport or content size changes such that not enough content is rendered. We should also expose a public method to allow users to trigger this manually
I ended up implementing this in my usages:
fromEvent(window, 'resize')
.pipe(
distinctUntilChanged(),
debounceTime(10),
sampleTime(0, animationFrameScheduler),
takeUntil(this._destroy$)
).subscribe(() => this.onResize());
Here is a temporary and crappy workaround. Probably needs a throttle, debounce, or something:
const ngOnInit = CdkVirtualScrollViewport.prototype.ngOnInit;
CdkVirtualScrollViewport.prototype.ngOnInit = function () {
ngOnInit.apply(this, arguments);
this['_resizeSubscription'] = fromEvent(window, 'resize')
.subscribe(() => this.checkViewportSize());
}
const ngOnDestroy = CdkVirtualScrollViewport.prototype.ngOnDestroy;
CdkVirtualScrollViewport.prototype.ngOnDestroy = function () {
ngOnDestroy.apply(this, arguments);
this['_resizeSubscription'].unsubscribe();
}
Here's how I temporarily fixed it, waiting for the official fix:
@Directive({
selector: 'cdk-virtual-scroll-viewport',
})
export class CdkVirtualScrollViewportPatchDirective implements OnInit, OnDestroy {
protected readonly destroy$ = new Subject();
constructor(
@Self() @Inject(CdkVirtualScrollViewport) private readonly viewportComponent: CdkVirtualScrollViewport,
) {}
ngOnInit() {
fromEvent(window, 'resize')
.pipe(
debounceTime(10),
takeUntil(this.destroy$),
)
.subscribe(() => this.viewportComponent.checkViewportSize())
;
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Still no fix for this? This is one of the first things I noticed when porting my library over to this virtual scrolling
Any solution???
The virtual scrolling feature is so useful and I think it should manage the container resizing.
Also hit this here using a variation of the linked issue #13981 (same root cause - viewport size should not be computed when the tab containing the virtual scrolling component is not activated).
In the meantime an application-only workaround for tab-based use could be to wrap the content under an *ngIf, if the tab is not activated so viewport calculation is not completed prematurely. Still, the above workarounds are preferable, so will try that first. Thanks to @grant77 and @jlowcs for that snippet 馃憤.
@jlowcs's Directive works great to solve the window resizing issue. It, however, doesn't solve the issue when switching tabs when the virtual scrolling component is used in a mat-tab component. I'll need to figure out a similar mechanism which triggers checkViewportSize() on tab change.
@jlowcs's Directive works great to solve the window resizing issue. It, however, doesn't solve the issue when switching tabs when the virtual scrolling component is used in a mat-tab component. I'll need to figure out a similar mechanism which triggers checkViewportSize() on tab change.
I did the following workaround as below:
On tabChangeAction => I trigger
tabChangeAction(index: number) {
this.cityTab.onVisible(true);
}
In the cityTab component ,if tab is visible, then dispatch resize event to trigger for checkViewportSize.
onVisible(flag: boolean) {
if (flag) {
window.dispatchEvent(new Event('resize'));
}
}
@jlowcs first this is a good solution, but i dont see any reason angular cdk not handle this by its own should be a bug / error. If u have multiple Directives / Components or whatever on page which should react on a window resize u have to register multiple Window.Resize Events.
I think it is better to create 1 Resize Event and share this Event through Observable with all who are interested in this. So u will allways have only 1 resize event and this should be better in my opinion.
I create a window resize service for my own projects to register only one listener which will be shared.
And could registered like this, and at this point it is very easy to update cdk-virtual-scroll-viewport
export class MyComponent {
constructor(
private windowResize: WindowResize
) {
}
public ngOnInit() {
this.windowResize.onChange()
.pipe( takeUntil( this.destroy$ ) )
.subscribe(() => {
// window size has changed
});
}
Still waiting for official solution
Reproduction of the problem: https://stackblitz.com/edit/components-issue-ljc2ov?file=app/app.component.ts
We've implemented an extension directive to handle these situations, easily reusable and doesn't require pollution of the parent controller class.
Found a super easy, albeit hacky way to fix this. I've found you don't have to have the exact size of the itemSize property correct, if you change the value when the viewport changes, then it will refresh the number to display... it would be even easier but I had to use setTimeout to avoid the dreaded expression has changed after view checked.
template
<cdk-virtual-scroll-viewport [itemSize]="itemSize"
component
export class VirtualScrollProfilesComponent implements OnInit {
virtualPersons = [];
itemSize = 50;
ngOnInit() {
this.determineItemSize();
}
private determineItemSize() {
setTimeout(() => {
this.itemSize = 50 - this.windowService.boostrapColumns;
})
}
/**
* resize columns on windowResize
* @param event resize event
*/
@HostListener('window:resize')
onResize() {
this.determineItemSize();
}
}
My virtual scroll component is sitting inside an expansion panel which changes the size of the viewport independent from the window size.
For everybody else facing that problem:
I ended up using css-element-queries and connecting the CdkVirtualScrollViewport to the ResizeSensor in the AfterViewInit hook. Now the viewport can be rerenderd on any size change, wherever it comes from.
@jlowcs's Directive works great to solve the window resizing issue. It, however, doesn't solve the issue when switching tabs when the virtual scrolling component is used in a mat-tab component. I'll need to figure out a similar mechanism which triggers checkViewportSize() on tab change.
I did the following workaround as below:
On tabChangeAction => I trigger
tabChangeAction(index: number) {
this.cityTab.onVisible(true);
}In the cityTab component ,if tab is visible, then dispatch resize event to trigger for checkViewportSize.
onVisible(flag: boolean) {
if (flag) {
window.dispatchEvent(new Event('resize'));
}
}
Dispatching the 'resize' event is working well for my use case.
window.dispatchEvent(new Event('resize'));
Most helpful comment
Here's how I temporarily fixed it, waiting for the official fix: