在cdk-virtual-scroller中获取静态组件引用? (参考文献被回收) [英] Obtaining a static component reference within a cdk-virtual-scroller? (References are recycled)

查看:272
本文介绍了在cdk-virtual-scroller中获取静态组件引用? (参考文献被回收)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We recently transitioned our scrollable lists to CDK Virtual Scroller. (Angular 7.2.12 with angular/cdk 7.3.7)

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

In short, it seems that the VirtualScrollViewport is recycling component instances, not just the template as the documentation suggests.

StackBlitz上的实时MCVE (已更新以反映编辑1).

Live MCVE on StackBlitz (updated to reflect EDIT 1).

编辑1

一位同事提醒我,我们现在使用的是命名引用,而不是ViewChildren(),就像这样:

A colleague reminded me that we're now using named references instead of ViewChildren(), like so:

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中实现它,例如:

And implementing it in the App like:

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

它将被单击的元素的背景颜色更改为红色,但是在滚动时,其他元素(大概是那些与某些缓存索引匹配的元素?)将已经是红色!激活其中之一也会清除原始内容.

It will change the background colour of the clicked element to red, but when scrolling, others (presumably those that match some cached index?) will already be red! Activating one of those will clear the original as well.

消息来源建议 templateCacheSize可能有帮助,但没有帮助.

The source suggests that templateCacheSize might help, but it doesn't.

原始

可滚动区域包含我们可以通过@ViewChildren()QueryList进行引用的组件,并使用*ngFor(现在为*cdkVirtualFor)中的索引来跟踪正在操作的组件,如下所示:

The scrollable area contains components which we get a reference to with a @ViewChildren() and QueryList and we track which one we are acting on using an index in the *ngFor (now *cdkVirtualFor), like so:

<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>

然后,从页面中,我们与列表中的组件进行通信:

Then, from the page, we communicate with the component in the list:

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]抛出ReferenceError.

Of course, now that the template is rendered in a virtual scroll container, only the first n are rendered into the DOM. So if you scroll down the list beyond what is initially loaded, hiRefs does not contain a reference to the item with the corresponding index, throwing a ReferenceError for the provided ["_results"][i].

我尝试了trackBy,但没有取得任何成果.

I experimented with trackBy but didn't get anything fruitful.

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

EDIT: A colleague has also attempted to pass a named reference, which curiously has the same problem.

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

并在App中实现它,例如:

And implementing it in the App like:

<hello #hi [item]="item" (click)="clickByReference(hi)"></hello>

它将被单击的元素的背景颜色更改为红色,但是在滚动时,其他元素(可能是与相同索引匹配的元素)将已经是红色,尽管未使用 @ViewChildren() <完全是c6>!

It will change the background colour of the clicked element to red, but when scrolling, others (presumably those that match the same index) will already be red, despite not using the @ViewChildren() QueryList at all!

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

It seems that the CDK is recycling component instance references?

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

I updated the StackBlitz with the method clickByReference(), and renamed the one above to clickByIndex().

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

How can I correctly get a reference to the component in the list in order to call methods on it?

推荐答案

默认情况下,CdkVirtualForOf

By default, CdkVirtualForOf caches 20 ViewRefs to components that are no longer rendered into the DOM to improve scrolling performance.

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

While these update to show new bound @Input()s, they do not update their internal state, so previously-cached copies are re-used as a result.

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

It seems the only solution is to set templateCacheSize: 0:

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

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

That way the components are destroyed once they're no longer visible, and state is lost.

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

这篇关于在cdk-virtual-scroller中获取静态组件引用? (参考文献被回收)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆