Open yogeshgadge opened 6 years ago
Workaround:-
https://stackblitz.com/edit/angular-h4xptu-zyj4yh?file=styles.css
Added class virtual-scroll
to mat-select
<mat-select placeholder="State" class="virtual-scroll">
and add global styles as below:-
In styles.css
add
.mat-select-panel.virtual-scroll {
max-height: 100% !important;
overflow: inherit !important;
}
.mat-select-panel .cdk-virtual-scroll-viewport {
max-height: 240px !important;
}
.mat-select-panel .cdk-virtual-scroll-content-wrapper {
position: inherit !important;
top: inherit !important;
left: 0;
}
I think the real solution would be that somehow mat-select-panel
ideally starts behaving like cdk-virtual-scroll-viewport
which is what this workaround crudely tries to do in totality by negating scrolling properties of mat-select-panel
and having cdk-virtual-scroll-viewport
take charge.
Not quite the workaround above: The above workaround has problems when the overlay is closed and reopened and scrolled in between.
When scrolled to a few elements down and the select panel is reopened next time we see a blank space above.
This is solved by setRenderedRange
to the initial range which gets set initially on the scrolledIndexChange
event.
Then further when the mat-select emits openedChange
on close we reset the rendered range to the initialRange
that we preserved upfront.
https://stackblitz.com/edit/angular-h4xptu-kuu5xg?file=app%2Fselect-reset-example.ts
scrolledIndexChange($event) {
if (!this.initialRange) {
this.initialRange = this.cdkVirtualScrollViewport.getRenderedRange();
}
}
openedChange(opened) {
if (!opened) {
this.cdkVirtualScrollViewport.setRenderedContentOffset(0);
this.cdkVirtualScrollViewport.setRenderedRange(this.initialRange)
}
}
Another issue with mat-select
(for anyone working on this):-
If the currently selected value is not in the mat-options
the SelectionModel
fails because the world according it is limited to the options
query which may not have this options if it is out of range.
Workaround:
Supplement additional mat-option
in addition to mat-option
that has cdkVirtualScroll
.
<mat-option *ngIf="selectCtrl?.value" [value]="selectCtrl?.value"></mat-option>
Related to https://github.com/angular/material2/issues/10122
@yogeshgadge Thanks for posting these findings, good stuff to know.
workarround for mat select using @yogeshgadge s approach with a form control
(css in stylus syntax)
<ng-container *ngIf="isMultiple">
<mat-option class="selected-options-bottom" *ngFor="let option of control.value" [value]="option">{{option.caption}}</mat-option>
</ng-container>
<ng-container *ngIf="!isMultiple">
<mat-option class="selected-options-bottom" *ngIf="control.value" [value]="control.value">{{control.value.caption}}</mat-option>
</ng-container>
with css:
.selected-options-bottom
visibility hidden
position absolute
giving the mat-select a panelClass, e.g. custom-mat-select with
::ng-deep .mat-select-panel.custom-mat-select
overflow hidden
max-width 280px
I just gave the example in the issue desription a quick look - itemSize should be the height of individual items, not the length of the array? Setting a height on the
Also cdk-virtual-scroll-viewport inside a mat-menu doesn't work properly. Sometimes, blank space appears with mouse wheel scrollling. StackBlitz
@StefanoLucchi Same thing happen to me. And when you reopen the menu it's empty
@StefanoLucchi @justindiaw I had the same problem, but helped me increasing minBufferPx and maxBufferPx on cdk-virtual-scroll-viewport to eg. 300 and 600
A slight change to @yogeshgadge's solution and changing the buffers as suggested by @kubex320 solved all the white space issues.
<mat-select
(openedChange)="onOpenedChange($event)"
>
<cdk-virtual-scroll-viewport
[itemSize]="optionSizePx"
[style.height.px]="numOptionsToShow * optionSizePx"
[minBufferPx]="300"
[maxBufferPx]="600"
>
<mat-option
*cdkVirtualFor="let item of listOptions | async; let index = index"
[value]="item.value"
(onSelectionChange)="onSelectionChange(index)"
>
{{item.viewValue}}
</mat-option>
</cdk-virtual-scroll-viewport>
</mat-select>
@Input()
optionSizePx = 48;
@Input()
numOptionsToShow = 5;
@ViewChild(CdkVirtualScrollViewport, {static: true})
cdkVirtualScrollViewport: CdkVirtualScrollViewport;
selectedIndex: number;
onOpenedChange(isOpen) {
if (isOpen && this.selectedIndex) {
this.cdkVirtualScrollViewport.setRenderedContentOffset(0);
this.cdkVirtualScrollViewport.scrollToIndex(this.selectedIndex);
}
}
onSelectionChange(i: number) {
this.selectedIndex = i;
}
Not quite the workaround above: The above workaround has problems when the overlay is closed and reopened and scrolled in between.
When scrolled to a few elements down and the select panel is reopened next time we see a blank space above.
This is solved by
setRenderedRange
to the initial range which gets set initially on thescrolledIndexChange
event. Then further when the mat-select emitsopenedChange
on close we reset the rendered range to theinitialRange
that we preserved upfront.https://stackblitz.com/edit/angular-h4xptu-kuu5xg?file=app%2Fselect-reset-example.ts
scrolledIndexChange($event) { if (!this.initialRange) { this.initialRange = this.cdkVirtualScrollViewport.getRenderedRange(); } } openedChange(opened) { if (!opened) { this.cdkVirtualScrollViewport.setRenderedContentOffset(0); this.cdkVirtualScrollViewport.setRenderedRange(this.initialRange) } }
Exactly what I was looking for. Thank you. I was wondering why the panel seems to show white div space when scroll in between and close and reopen again.
Thanks to those who shared workarounds - it's very helpful! Anyone got the keyboard navigation (up/down arrow keys to browse through the options) working with virtual scrolling? It doesn't scroll when I use keyboard and I couldn't figure out how to get this working. I'm thinking of implementing custom keyboard event handlers for up/down arrow keys and manually setting the virtual scroll position (or the rendered index), but not sure if it's the best way to do as mat-select already has the keyboard handlers implemented. Would appreciate any inputs on this :)
I had a issue with only seeing a white dropdown after I selected a option and reopened the select. This only happened when I selected an option where I needed to scroll to get to.
I solved it purely in the template and just want to leave this here for reference in case someone else stumbles across this:
It's basically just a combination of a template variable and calling scrollToIndex
on it
<mat-form-field>
<mat-select [formControl]="form" placeholder="State" class="virtual-scroll"
(openedChange)="scrollViewport.scrollToIndex(states.indexOf(form.value))">
<cdk-virtual-scroll-viewport itemSize="10" #scrollViewport>
<mat-option *cdkVirtualFor="let state of states" [value]="state">
{{state}}
</mat-option>
</cdk-virtual-scroll-viewport>
</mat-select>
</mat-form-field>
https://stackblitz.com/edit/angular-h4xptu-mmtdbj?file=app%2Fselect-reset-example.html
Another smaller issue I saw here was that mat-active is being inappropriately assigned to options in the virtual scrolling component. Using yogeshgadge's stackblitz (https://stackblitz.com/edit/angular-h4xptu-zyj4yh?file=styles.css) you can see that if you select the first option and then reopen the dropdown and start scrolling down, every 11th or so option will have the mat-active class set on it.
Thanks to those who shared workarounds - it's very helpful! Anyone got the keyboard navigation (up/down arrow keys to browse through the options) working with virtual scrolling? It doesn't scroll when I use keyboard and I couldn't figure out how to get this working. I'm thinking of implementing custom keyboard event handlers for up/down arrow keys and manually setting the virtual scroll position (or the rendered index), but not sure if it's the best way to do as mat-select already has the keyboard handlers implemented. Would appreciate any inputs on this :)
Did you ever figure out a way to do this? I'm currently trying to implement this, but it seems like set scrollToIndex or setRenderedRange stops the next option from being selected/highlighted/focused.
@dickinr I found that all of the solutions above result in a hacky solution. I looked at the source code, but couldn't find a reasonable way of getting the virtual scrolling to work, without rewriting the component. I think it would be best to either create a custom component, or to use a different UI component. Maybe a filtered autocomplete would fit your purposes: https://material.angular.io/components/autocomplete/examples
Hello,
We are using Angular 16 and still facing issue.
If we select an item wich is not in default rendered view range (0 to N), the viewport appears blank until a scroll event.
Is there some news ?
Bug, feature request, or proposal:
Bug
What is the expected behavior?
cdk-virtual-scroll-viewport
inside a mat-select or mat-autocomplete should align with the overlay panel.What is the current behavior?
When scrolled
cdkVirtualFor
does not seem to add more elements and moreover shows a big space from last element of the initial set of items to the end.What are the steps to reproduce?
https://stackblitz.com/edit/angular-h4xptu-dgjd87?file=app/select-reset-example.html
What is the use-case or motivation for changing an existing behavior?
Occasionally I run into scenarios where the items in the list are too many and makes the page unresponsive.
Seem-less integration of cdk-virtual-scroll-viewport with other components.
mat-select essentially can handle any number of elements when this works.
Which versions of Angular, Material, OS, TypeScript, browsers are affected?
Is there anything else we should know?
Great work