在cdk-virtual-scroller中获取静态组件引用? (参考文献被回收)

时间:2019-04-15 19:30:23

标签: angular angular-cdk angular-cdk-virtual-scroll

我们最近将可滚动列表转换为CDK Virtual Scroller。 (Angular 7.2.12和angular / cdk 7.3.7)

简而言之,似乎VirtualScrollViewport正在回收组件实例,而不仅仅是文档建议的模板。

Live MCVE on StackBlitz(已更新,以反映EDIT 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;
}

并在App中实现它,例如:

<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 suggeststemplateCacheSize可能有帮助,但没有帮助。

原始

可滚动区域包含我们可以通过@ViewChildren()QueryList进行引用的组件,并使用*ngFor(现在为{{ 1}}),就像这样:

*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}`); } 会呈现到DOM中。因此,如果您向下滚动列表以超出最初加载的内容,则n将不包含对具有相应索引的项目的引用,并为提供的hiRefs抛出ReferenceError

我尝试了["_results"][i],但没有取得任何成果。

编辑:一位同事还试图传递一个命名的引用,奇怪的是,它也有同样的问题。

trackBy更新为

HelloComponent

并在App中实现它,例如:

@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;
}

它将被单击的元素的背景色更改为红色,但是在滚动时,其他元素(大概是与相同索引匹配的元素)将已经是红色,尽管不使用 {{1} } <hello #hi [item]="item" (click)="clickByReference(hi)"></hello>

CDK似乎正在回收组件实例引用?

我用方法@ViewChildren()更新了StackBlitz,并将上面的方法重命名为QueryList

如何正确引用列表中的组件以调用其上的方法?

1 个答案:

答案 0 :(得分:0)

默认情况下,CdkVirtualForOf caches 20 ViewRefs用于不再渲染到DOM中以提高滚动性能的组件。

虽然这些更新显示了新的绑定@Input(),但它们不会更新其内部状态,因此,结果是先前使用的副本被重新使用。

似乎唯一的解决方案是设置templateCacheSize: 0

<ng-container *cdkVirtualFor="let item of data; templateCacheSize: 0">

这样,一旦不再可见这些组件便被销毁,并且状态丢失。

进一步阅读https://github.com/angular/material2/issues/15838