yglodt
yglodt

Reputation: 14551

cdk-virtual-scroll-viewport with variable item heights

I would like to use cdk-virtual-scroll-viewport in a TimeLine view with items of different heights.

So setting itemSize="x" which, according to the documentation refers to The size of the items in the list (in pixels), is unpractical.

autosize is not yet available.

Is it possible at all to use virtual/endless scrolling with cdk-virtual-scroll-viewport vith variable item sizes?

Update

I was looking for alternative virtual/endless scrolling solutions and, I hardly can believe, it seems there is no solution which works with dynamic row height, even with https://github.com/rintoj/ngx-virtual-scroller it's not recommended.

Update 2, July 2019

Since meanwhile there is still no solution, I believe the "good enough" way to work around this would be to load a fixed number of items, and add a button to load more items at the bottom of the list, like in this example: https://stackblitz.com/edit/ang-mat-load-more

Upvotes: 24

Views: 39199

Answers (8)

Reza Taba
Reza Taba

Reputation: 1914

I tried @angular/cdk-experimental with autosize but didn't work with Angular 18 with the error, cdk-virtual-scroll: scrolledIndexChange is currently not supported for the autosize scroll strategy. Most other solutions are partial.

So here's my complete SOLUTION:

  1. Create a directive with CLI (e.g. in directives folder):
ng g d directives/cdk-dynamic-height
  1. Add this code in it

    import { Directive, ElementRef, AfterViewInit, OnDestroy, Input } from '@angular/core';
    
    @Directive({
     standalone: true,
     selector: '[appCdkDynamicHeightDir]'
    })
    export class CdkDynamicHeightDirective implements AfterViewInit, OnDestroy {
     @Input() appCdkDynamicHeightDir: { height: number } | undefined;
     private resizeObserver: ResizeObserver | undefined;
    
     constructor(private elementRef: ElementRef) { }
    
     ngAfterViewInit() {
         this.resizeObserver = new ResizeObserver(entries => {
             for (const entry of entries) {
                 if (this.appCdkDynamicHeightDir && entry.contentRect.height !== this.appCdkDynamicHeightDir.height) {
                     this.appCdkDynamicHeightDir.height = entry.contentRect.height;
                 }
             }
         });
         this.resizeObserver.observe(this.elementRef.nativeElement);
     }
    
     ngOnDestroy() {
         if (this.resizeObserver) {
             this.resizeObserver.disconnect();
         }
     }
    }
    
  2. Import CdkDynamicHeightDirective to your Modules/Component.

  3. Use the [appCdkDynamicHeightDir]="item" in the DOM with *cdkVirtualFor

    <cdk-virtual-scroll-viewport 
       (scrolledIndexChange)="loadMoreMessages($event)" 
       [minBufferPx]="300" [maxBufferPx]="400" [itemSize]="50">
    
       <div *cdkVirtualFor="let item of items" [appCdkDynamicHeightDir]="item" class="item">
         <!-- Your code here... -->
       </div>
    
    </cdk-virtual-scroll-viewport>
    
  4. Set a minimum default CSS height but let it grow as needed.

    .item {
        min-height: 50;
    }
    
  5. Enjoy!

Upvotes: 1

Uri Gross
Uri Gross

Reputation: 524

The only thing that worked for me is the example of splaktar at stackblitz

Template:

<cdk-virtual-scroll-viewport [itemSize]="itemSize" class="example- 
viewport">
<div *cdkVirtualFor="let item of items"
   [style.height]="itemSize + 'px'">{{item}}</div>
</cdk-virtual-scroll-viewport>
<br>
<div>
<label for="height">Item Size:</label>
<input id="height" type="number" [(ngModel)]="itemSize">
</div>

component:

import {ChangeDetectionStrategy, Component} from '@angular/core';

@Component({
selector: 'cdk-virtual-scroll-overview-example',
styleUrls: ['cdk-virtual-scroll-overview-example.css'],
templateUrl: 'cdk-virtual-scroll-overview-example.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CdkVirtualScrollOverviewExample {
items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
itemSize = 80;
}

Upvotes: 0

Agung Sudrajat
Agung Sudrajat

Reputation: 328

Finally I found the solution: In my case, I recently use @angular/cdk version 12.2.13 and so you should install too @angular/cdk-experimental with version 12.2.13 (the same version).

Go to app.module.ts: add this line:

import { ScrollingModule as ExperimentalScrollingModule } from '@angular/cdk-experimental/scrolling';
import { ScrollingModule } from '@angular/cdk/scrolling';

and in imports:

[ScrollingModule,
    ExperimentalScrollingModule]

Then do this:

<cdk-virtual-scroll-viewport
          autosize
          style="height: 100%"
          class="container"
        >
your content
</cdk-virtual-scroll-viewport>

it worked for me.

Upvotes: 4

Dionis Oros
Dionis Oros

Reputation: 672

autosize works for me.

Try to install:

"@angular/cdk": "6.2.0",
"@angular/cdk-experimental": "6.2.0"

and then import ScrollingModule into your module:

import {ScrollingModule} from "@angular/cdk-experimental";

imports: [ScrollingModule]

then you can use autosize property like below:

 <cdk-virtual-scroll-viewport autosize style="height: 100%">

Upvotes: 11

Rinkesh Patel
Rinkesh Patel

Reputation: 72

This CSS works for me. No fixed height required:

<cdk-virtual-scroll-viewport class="viewport">  
   .......
</cdk-virtual-scroll-viewport>

.viewport { 
   display: contents;
}

Upvotes: -1

sai amar
sai amar

Reputation: 1848

It is possible to set cdkvirtualscrollviewport height dynamically with [ngStyle]

          <cdk-virtual-scroll-viewport
        itemSize="parent?.children.length"
        [ngStyle]="{'height.px': parent?.children.length<10? parent?.children.length*42 :250 }"
      >

Upvotes: -1

Richard Medeiros
Richard Medeiros

Reputation: 978

Until this feature is offered in the CDK I got around it by listening to the onscroll event of the native element then respond when the scroll offset to the bottom is 0

@ViewChild(CdkVirtualScrollViewport, { static: false })
public virtualScrollViewport?: CdkVirtualScrollViewport;

...

ngAfterViewInit() {
  this.virtualScrollViewport.elementRef.nativeElement.onscroll = (e) => { this.onScroll(e) };
}

onScroll(e) {
  var scrollOffset = this.virtualScrollViewport.measureScrollOffset("bottom");

  if (scrollOffset == 0) {
    this.fetchMoreData();
  }
}

Upvotes: 2

Akber Iqbal
Akber Iqbal

Reputation: 15041

itemSize="x" doesn't help set the height... you'd have to use CSS to assign an arbitary height yourself.

coming to your question, variable item sizes should not be a problem with the virtual scroll... you can change the array in this example to see the possibility & results very quickly.

Upvotes: 0

Related Questions