Cdk virtual-scroll inside mat-select for mat-option
Asked Answered
R

3

13

Has anyone been able to use virtual-scroll inside mat-select as shown below ?

<mat-form-field>
    <mat-select placeholder="State">
        <cdk-virtual-scroll-viewport autosize>
            <mat-option *cdkVirtualFor="let state of states" [value]="state">{{state}}</mat-option>
        </cdk-virtual-scroll-viewport>
    </mat-select>
</mat-form-field>

As you can see https://stackblitz.com/edit/angular-h4xptu?file=app%2Fselect-reset-example.html it does not work - causes weird blank space as you scroll.

Raymond answered 10/9, 2018 at 22:44 Comment(0)
R
10

I think i have solved this:

https://stackblitz.com/edit/angular-gs4scp

The key things are when the mat select opens panel we trigger cdkVirtualScrollViewPort scroll and check view port size.

  openChange($event: boolean) {
    console.log("open change", $event);
    if ($event) {
      this.cdkVirtualScrollViewPort.scrollToIndex(0);
      this.cdkVirtualScrollViewPort.checkViewportSize();
    } else {
    }
  }

Where we get the reference to the virtual scroll viewport using @ViewChild

@ViewChild(CdkVirtualScrollViewport, { static: false })
  cdkVirtualScrollViewPort: CdkVirtualScrollViewport;

Other relevant pieces in the template are are pretty simple:-

<mat-form-field>
    <mat-select [formControl]="itemSelect"
  placeholder="Select Item"
  (openedChange)="openChange($event)">
    <mat-select-trigger>
      {{ itemTrigger }}
    </mat-select-trigger>
        <cdk-virtual-scroll-viewport itemSize="5" minBufferPx="200" maxBufferPx="400" class="example-viewport-select">
            <mat-option *cdkVirtualFor="let item of items" [value]="item"
                (onSelectionChange)="onSelectionChange($event)">{{item}}</mat-option>
        </cdk-virtual-scroll-viewport>
    </mat-select>
    <mat-hint>Justa hint</mat-hint>
</mat-form-field>
Raymond answered 20/11, 2019 at 1:1 Comment(3)
Good start, doesn't handle keyboard inputs properly... i.e. the viewport does not get scrolled..Brythonic
does this scroll to the currently active item?Patin
Find the index and set the this.cdkVirtualScrollViewPort.scrollToIndex(selectedIndex); in openChange instead of 0 as shown in the snippet/ That should work.Raymond
S
10

The virtual scroll viewport needs a size in order to know, how big the scroll container must be. This can be done by specifying the [itemSize] property of <cdk-virtual-scroll-viewport> and its height.

In your example the height of one <option> item is 48px. If you want to show five items at once, the container size would be 5 * 48 = 240:

<mat-form-field>
    <mat-select placeholder="State">
        <cdk-virtual-scroll-viewport [itemSize]="48" [style.height.px]=5*48>
            <mat-option *cdkVirtualFor="let state of states" [value]="state"> 
                {{state}}
            </mat-option>
        </cdk-virtual-scroll-viewport>
    </mat-select>
</mat-form-field>
Sarson answered 5/12, 2018 at 14:26 Comment(5)
Blank space is still there stackblitz.com/edit/… . Try this scroll down, close the drop down, then open again. You can head over to github.com/angular/material2/issues/13087 opened and workarounds but there is a inherent problem hence the item is still open.Raymond
Perhaps we can combine yours with github.com/angular/material2/issues/…Raymond
I don't get it. My solution works perfectly fine. As @yusijs stated in this issue, the itemSize property is the height in pixels of one item -- not the array length. I wouldn't use autosize, because it is not stable, yet.Sarson
Try this (your solution) stackblitz.com/edit/angular-h4xptu-dy6nml?file=app/… steps: 1) Open drop down 2) scroll all the way down 3) close drop down 4) Open drop down...I am seeing a big blank drop down. What are you seeing ?Raymond
Yep, I see it. Must be a bug -- this is certainly not intended.Sarson
R
10

I think i have solved this:

https://stackblitz.com/edit/angular-gs4scp

The key things are when the mat select opens panel we trigger cdkVirtualScrollViewPort scroll and check view port size.

  openChange($event: boolean) {
    console.log("open change", $event);
    if ($event) {
      this.cdkVirtualScrollViewPort.scrollToIndex(0);
      this.cdkVirtualScrollViewPort.checkViewportSize();
    } else {
    }
  }

Where we get the reference to the virtual scroll viewport using @ViewChild

@ViewChild(CdkVirtualScrollViewport, { static: false })
  cdkVirtualScrollViewPort: CdkVirtualScrollViewport;

Other relevant pieces in the template are are pretty simple:-

<mat-form-field>
    <mat-select [formControl]="itemSelect"
  placeholder="Select Item"
  (openedChange)="openChange($event)">
    <mat-select-trigger>
      {{ itemTrigger }}
    </mat-select-trigger>
        <cdk-virtual-scroll-viewport itemSize="5" minBufferPx="200" maxBufferPx="400" class="example-viewport-select">
            <mat-option *cdkVirtualFor="let item of items" [value]="item"
                (onSelectionChange)="onSelectionChange($event)">{{item}}</mat-option>
        </cdk-virtual-scroll-viewport>
    </mat-select>
    <mat-hint>Justa hint</mat-hint>
</mat-form-field>
Raymond answered 20/11, 2019 at 1:1 Comment(3)
Good start, doesn't handle keyboard inputs properly... i.e. the viewport does not get scrolled..Brythonic
does this scroll to the currently active item?Patin
Find the index and set the this.cdkVirtualScrollViewPort.scrollToIndex(selectedIndex); in openChange instead of 0 as shown in the snippet/ That should work.Raymond
M
-1

Add an additional mat-option using *ngFor as if you were not using cdk virtual scroll. This does not add any additional options to your select list, but does allow a default selected value, while also allowing cdk-virtual scroll to do its thing.

<mat-select placeholder="State">
  <cdk-virtual-scroll-viewport itemSize="10" [style.height.px]=10*100>
        <mat-option *cdkVirtualFor="let state of states" [value]="state">{{state}}</mat-option>
  </cdk-virtual-scroll-viewport>
  <mat-option *ngFor="let state of states" [value]="state"> 
  {{state}}</mat-option>
 </mat-select>
Murmur answered 18/2, 2022 at 17:18 Comment(2)
This makes no sense. It will add the whole list to the dom again. You can't see them visually, but they are there. Which will again choke the browser if the list if huge. This defeats the purpose of using the virtual scroll in the first placeBacchius
If you have a better working option, we would all appreciate it. The solution that was voted up did not work for me.Murmur

© 2022 - 2024 — McMap. All rights reserved.