我有一个多选下拉菜单,里面有虚拟滚动条。
HTML
<mat-form-field>
<mat-label>Toppings</mat-label>
<mat-select [formControl]="multiSelectControl" multiple [(value)]="selected" (openedChange)="openChange($event)">
<cdk-virtual-scroll-viewport itemSize="5" minBufferPx="200" maxBufferPx="400" [style.height.px]=5*48>
<button (click)="selectAll()">Select All</button>
<button (click)="clear()">Clear</button>
<mat-option *cdkVirtualFor="let topping of toppingList" [value]="topping">{{topping}}</mat-option>
</cdk-virtual-scroll-viewport>
</mat-select>
</mat-form-field>
.ts 文件:
export class AppComponent {
title = 'test-proj';
toppings = new FormControl();
toppingList: string[] = ['Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato'];
selected: any;
@ViewChild(CdkVirtualScrollViewport)
cdkVirtualScrollViewPort: CdkVirtualScrollViewport;
multiSelectControl = new FormControl();
constructor() {
for (let i = 0; i < 4000; i++) {
this.toppingList.push('gjkgkf--' + i);
}
}
selectAll() {
this.selected = this.toppingList;
this.multiSelectControl.patchValue(this.toppingList);
}
clear() {
this.selected = [];
this.multiSelectControl.patchValue([]);
}
openChange($event: boolean) {
if ($event) {
this.cdkVirtualScrollViewPort.scrollToIndex(0);
this.cdkVirtualScrollViewPort.checkViewportSize();
}
}
我在下拉列表中添加了 4000 个项目。如果我选择前两项和最后两项并滚动,则前两项的选择检查就会消失。
请提出建议。谢谢
我已经为错误本身创建了一种解决方法。它使用
onSelectionChange
元素的 mat-option
。最重要的是,它还会检查选项列表上的更改,并在必要时进行选择。这是未经优化的原始代码,但您会明白的:
@ViewChildren(MatOption)
options: QueryList<MatOption>;
constructor(private cd: ChangeDetectorRef) {}
ngAfterViewInit(): void {
this.options.changes.subscribe(() => {
let needUpdate = false;
this.options.forEach((option) => {
const selected = this.selected.includes(option.value);
if (selected && !option.selected) {
option.select();
needUpdate = true;
} else if (!selected && option.selected) {
option.deselect();
needUpdate = true;
}
});
if (needUpdate) {
this.cd.detectChanges();
}
});
}
onSelectionChange(change): void {
if (!change.isUserInput) {
return;
}
const value = change.source.value;
const idx = this.selected.indexOf(change.source.value);
if (idx > -1) {
this.selected.splice(idx, 1)
} else {
this.selected.push(value);
}
}
这就是
mat-select
:
<mat-select [formControl]="multiSelectControl" multiple [value]="selected" (openedChange)="openChange($event)">
<cdk-virtual-scroll-viewport itemSize="5" minBufferPx="200" maxBufferPx="400" [style.height.px]=5*48>
<button (click)="selectAll()">Select All</button>
<button (click)="clear()">Clear</button>
<mat-option *cdkVirtualFor="let topping of toppingList" [value]="topping" (onSelectionChange)="onSelectionChange($event)">{{topping}}</mat-option>
</cdk-virtual-scroll-viewport>
</mat-select>
使用有效的 stackblitz
请注意,如果您有一个可以更新其内容的动态列表,并且已从该列表中删除了所做的选择,那么它仍然会被选择,即使这不再可能了
首先,确保导入ScrollingModule。
我相信答案是向您的 *cdkVirtualFor 添加一个 trackBy 函数
*cdkVirtualFor="let topping of toppingList; trackBy: trackByName; templateCacheSize: 0"
trackByName(index: number, item: string): string {
return item;
}
如果您有重复的名称,那么您可以尝试按索引进行跟踪,但我更喜欢为每个项目使用自定义 ID。一些能让他们独一无二的东西。
如果您正在处理动态数据,我还会添加
templateCacheSize: 0
。