5

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

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

StackBlitz 上的实时 MCVE(更新以反映 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;
}

并在应用程序中实现它,例如:

<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();
}

它将被点击元素的背景颜色更改为红色,但是在滚动时,其他元素(可能与某些缓存索引匹配的元素?)已经是红色了!激活其中一个也将清​​除原始文件。

消息来源暗示templateCacheSize可能会有所帮助,但它没有。

原来的

可滚动区域包含我们使用 a 引用的组件,@ViewChildren()并且我们使用(now )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不包含对具有相应索引的项目的引用,则会ReferenceError为提供的["_results"][i].

我尝试了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正在回收组件实例引用?

我用 方法更新了 StackBlitz clickByReference(),并将上面的重命名为clickByIndex().

如何正确获取对列表中组件的引用以便对其调用方法?

4

1 回答 1

8

默认情况下,将 20CdkVirtualForOf 缓存ViewRef到不再渲染到 DOM 中的组件,以提高滚动性能。

虽然这些更新以显示新的 bound @Input(),但它们不会更新其内部状态,因此先前缓存的副本会被重新使用。

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

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

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

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

于 2019-04-16T21:02:07.717 回答