可变宽度的CDK虚拟滚动
我想使用一个cdk-virtual-scroll-viewport,搭配包含大量项的列表。这个列表会在各处使用,有些项的文本比其他项多。我想设置一个最小宽度和最大宽度,这样当项的文本量较少时它只占用最小宽度,当文本量较多时则会扩展至最大宽度。
但我尝试的都不起作用。我试过大量的样式来达到我想要的效果,但到目前为止都没有成功。宽度只扩展到设定的最小宽度,即使文本应该继续扩展。我觉得这应该非常简单?有人能给点见解吗?
看看我在StackBlitz上设置的这个简单示例: https://stackblitz.com/edit/jrvmcqag?file=src%2Fapp%2Fcdk-virtual-scroll-append-only-example.html
(另外,我现在使用Angular 15,但计划升级。这也是StackBlitz中的示例基于Angular 15的原因。)
解决方案
你遇到的问题是,cdk-virtual-scroll-viewport 的内容使用了 position: absolute。因此,内容被认为具有 0 宽度,视口会调整到 min-width。
推荐的解决方案是将 viewport-wrapper 的宽度设为 max-width,并从 cdk-virtual-scroll-viewport 中移除 width: fit-content。
示例
如果你真的想让宽度随内容自适应,这当然可以,但这会带来非常奇怪的滚动行为,因为虚拟滚动仅在DOM中渲染一部分项。这意味着在你滚动查看项时,宽度会改变。
要实现这一点,你需要让虚拟滚动视口使用 position: relative。你必须把它放在 styles.css 中,并对 cdk-virtual-scroll-viewport 元素应用 flexible-width-virtual-scroll:
.cdk-virtual-scrollable.flexible-width-virtual-scroll {
contain: unset;
.cdk-virtual-scroll-content-wrapper {
position: relative;
}
}
然后你需要从视口的总大小中减去已渲染项的大小,这样 cdk-virtual-scroll-spacer 就具有正确的大小:
@ViewChild(CdkVirtualScrollViewport)
virtualScroll: CdkVirtualScrollViewport;
@ViewChild(CdkVirtualForOf)
virtualForOf: CdkVirtualForOf<any>;
nrOfRenderedItems = 0;
ngAfterViewInit() {
this.virtualForOf.viewChange.subscribe((viewRange) => {
if (viewRange.end - viewRange.start == this.nrOfRenderedItems) return;
this.nrOfRenderedItems = viewRange.end - viewRange.start;
this.virtualScroll.setTotalContentSize(
this.items.length * this.itemSize - this.nrOfRenderedItems * this.itemSize
);
});
}
示例
为了在滚动时防止宽度变化,你可以将视口的宽度设为当前集合中所有项的最大宽度。要得到这个宽度,你需要像 这个 那样,事先测量项中文本的宽度。