在 cdk-virtual-scroller 中获取静态组件引用? (参考文献被回收)
Obtaining a static component reference within a cdk-virtual-scroller? (References are recycled)
我们最近将可滚动列表转换为 CDK Virtual Scroller。 (Angular 7.2.12 与 angular/cdk 7.3.7)
简而言之,VirtualScrollViewport
似乎正在回收组件实例,而不仅仅是文档所建议的模板。
Live MCVE on StackBlitz(已更新以反映编辑 1)。
编辑 1
一位同事提醒我,我们现在使用命名引用而不是 ViewChildren()
,像这样:
HelloComponent
(*cdkVirtualFor
内):
@Component({
selector: 'hello',
template: `<h1 [class.active]="active">Data Item {{item}} !</h1>`,
styles: [`.active {background-color: red; color: white}`]
})
export class HelloComponent {
@Input() item: any;
active: boolean = false;
toggle = () => this.active = !this.active;
}
并在应用程序中实现它,例如:
<cdk-virtual-scroll-viewport itemSize="75">
<ng-container *cdkVirtualFor="let item of data" templateCacheSize=0>
<hello #hi [item]="item" (click)="clickByReference(hi)"></hello>
</ng-container>
</cdk-virtual-scroll-viewport>
// Non-essentials hidden, see StackBlitz
export class AppComponent {
data = Array.from(Array(100).keys())
clickByReference = (element: any): void => element.toggle();
}
它会将被点击元素的背景颜色更改为红色,但在滚动时,其他元素(大概是那些与某些缓存索引匹配的?)已经是红色的了!激活其中之一也将清除原始文件。
The source suggests templateCacheSize
可能有帮助,但没有帮助。
原创
可滚动区域包含我们使用 @ViewChildren()
和 QueryList
引用的组件,我们使用 *ngFor
中的索引跟踪我们正在操作的组件(现在*cdkVirtualFor
), 像这样:
<cdk-virtual-scroll-viewport itemSize="75">
<ng-container *cdkVirtualFor="let item of data; let i = index">
<hello #hi
[item]="item"
(click)="click(i)"></hello>
</ng-container>
</cdk-virtual-scroll-viewport>
然后,从页面中,我们与列表中的组件通信:
export class AppComponent {
@ViewChildren('hi') hiRefs: QueryList<HelloComponent>;
data = Array.from(Array(100).keys())
click = (i: number) => this.hiRefs["_results"][i].say(`Hello as ${i}`);
}
当然,既然模板是在虚拟滚动容器中渲染的,那么只有前 n
会渲染到 DOM 中。因此,如果您向下滚动列表超出最初加载的内容,hiRefs
不包含对具有相应索引的项目的引用,为提供的 ["_results"][i]
.[=37 抛出 ReferenceError
=]
我尝试了 trackBy
但没有取得任何成果。
编辑:一位同事也试图传递一个命名引用,奇怪的是它有同样的问题。
正在将 HelloComponent
更新为
@Component({
selector: 'hello',
template: `<h1 [class.active]="active">Data Item {{item}} !</h1>`,
styles: [`.active {background-color: red}`]
})
export class HelloComponent {
@Input() item: any;
active: boolean;
say = (something: any) => this.active = !this.active;
}
并在应用程序中实现它,例如:
<hello #hi [item]="item" (click)="clickByReference(hi)"></hello>
它会将被点击元素的背景颜色更改为红色,但是当滚动时,其他元素(大概是那些匹配相同索引的元素)已经是红色了,尽管没有使用 @ViewChildren()
QueryList
完全没有!
CDK 好像在回收组件实例引用?
我用 clickByReference()
方法更新了 StackBlitz,并将上面的重命名为 clickByIndex()
。
如何正确获取列表中组件的引用以便调用其上的方法?
默认情况下,CdkVirtualForOf
caches 20 ViewRef
s 将不再渲染到 DOM 的组件以提高滚动性能。
虽然这些更新显示新的绑定 @Input()
s,但它们不会更新其内部状态,因此先前缓存的副本因此被重新使用。
似乎唯一的解决办法是设置templateCacheSize: 0
:
<ng-container *cdkVirtualFor="let item of data; templateCacheSize: 0">
这样,组件一旦不再可见就会被销毁,状态也会丢失。
进一步阅读https://github.com/angular/material2/issues/15838 and a doc PR。
我们最近将可滚动列表转换为 CDK Virtual Scroller。 (Angular 7.2.12 与 angular/cdk 7.3.7)
简而言之,VirtualScrollViewport
似乎正在回收组件实例,而不仅仅是文档所建议的模板。
Live MCVE on StackBlitz(已更新以反映编辑 1)。
编辑 1
一位同事提醒我,我们现在使用命名引用而不是 ViewChildren()
,像这样:
HelloComponent
(*cdkVirtualFor
内):
@Component({
selector: 'hello',
template: `<h1 [class.active]="active">Data Item {{item}} !</h1>`,
styles: [`.active {background-color: red; color: white}`]
})
export class HelloComponent {
@Input() item: any;
active: boolean = false;
toggle = () => this.active = !this.active;
}
并在应用程序中实现它,例如:
<cdk-virtual-scroll-viewport itemSize="75">
<ng-container *cdkVirtualFor="let item of data" templateCacheSize=0>
<hello #hi [item]="item" (click)="clickByReference(hi)"></hello>
</ng-container>
</cdk-virtual-scroll-viewport>
// Non-essentials hidden, see StackBlitz
export class AppComponent {
data = Array.from(Array(100).keys())
clickByReference = (element: any): void => element.toggle();
}
它会将被点击元素的背景颜色更改为红色,但在滚动时,其他元素(大概是那些与某些缓存索引匹配的?)已经是红色的了!激活其中之一也将清除原始文件。
The source suggests templateCacheSize
可能有帮助,但没有帮助。
原创
可滚动区域包含我们使用 @ViewChildren()
和 QueryList
引用的组件,我们使用 *ngFor
中的索引跟踪我们正在操作的组件(现在*cdkVirtualFor
), 像这样:
<cdk-virtual-scroll-viewport itemSize="75">
<ng-container *cdkVirtualFor="let item of data; let i = index">
<hello #hi
[item]="item"
(click)="click(i)"></hello>
</ng-container>
</cdk-virtual-scroll-viewport>
然后,从页面中,我们与列表中的组件通信:
export class AppComponent {
@ViewChildren('hi') hiRefs: QueryList<HelloComponent>;
data = Array.from(Array(100).keys())
click = (i: number) => this.hiRefs["_results"][i].say(`Hello as ${i}`);
}
当然,既然模板是在虚拟滚动容器中渲染的,那么只有前 n
会渲染到 DOM 中。因此,如果您向下滚动列表超出最初加载的内容,hiRefs
不包含对具有相应索引的项目的引用,为提供的 ["_results"][i]
.[=37 抛出 ReferenceError
=]
我尝试了 trackBy
但没有取得任何成果。
编辑:一位同事也试图传递一个命名引用,奇怪的是它有同样的问题。
正在将 HelloComponent
更新为
@Component({
selector: 'hello',
template: `<h1 [class.active]="active">Data Item {{item}} !</h1>`,
styles: [`.active {background-color: red}`]
})
export class HelloComponent {
@Input() item: any;
active: boolean;
say = (something: any) => this.active = !this.active;
}
并在应用程序中实现它,例如:
<hello #hi [item]="item" (click)="clickByReference(hi)"></hello>
它会将被点击元素的背景颜色更改为红色,但是当滚动时,其他元素(大概是那些匹配相同索引的元素)已经是红色了,尽管没有使用 @ViewChildren()
QueryList
完全没有!
CDK 好像在回收组件实例引用?
我用 clickByReference()
方法更新了 StackBlitz,并将上面的重命名为 clickByIndex()
。
如何正确获取列表中组件的引用以便调用其上的方法?
默认情况下,CdkVirtualForOf
caches 20 ViewRef
s 将不再渲染到 DOM 的组件以提高滚动性能。
虽然这些更新显示新的绑定 @Input()
s,但它们不会更新其内部状态,因此先前缓存的副本因此被重新使用。
似乎唯一的解决办法是设置templateCacheSize: 0
:
<ng-container *cdkVirtualFor="let item of data; templateCacheSize: 0">
这样,组件一旦不再可见就会被销毁,状态也会丢失。
进一步阅读https://github.com/angular/material2/issues/15838 and a doc PR。