Получить ссылку на статический компонент в cdk-virtual-scroller? (Ссылки переработаны) - PullRequest
2 голосов
/ 15 апреля 2019

Мы недавно перевели наши прокручиваемые списки на CDK Virtual Scroller. (Угловой 7.2.12 с угловой / CDK 7.3.7)

Короче говоря, похоже, что VirtualScrollViewport - это утилизация экземпляров компонентов, а не только шаблона, как предполагает документация.

Live MCVE на 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();
}

Он изменит цвет фона элемента, на который нажали, на красный, но при прокрутке другие (предположительно те, которые соответствуют некоторому кешированному индексу?) Уже будут красными! Активация одного из них также очистит оригинал.

Источник предполагает , что 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}`);
}

Конечно, теперь, когда шаблон отображается в контейнере виртуальной прокрутки, в DOM отображаются только первые n. Таким образом, если вы прокрутите список ниже того, что было изначально загружено, 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() * 1069 не используется * вообще!

Кажется, что CDK перерабатывает ссылки на экземпляры компонентов?

Я обновил StackBlitz методом clickByReference() и переименовал вышеупомянутый в clickByIndex().

Как правильно получить ссылку на компонент в списке, чтобы вызывать методы для него?

1 Ответ

1 голос
/ 17 апреля 2019

По умолчанию CdkVirtualForOf кэширует 20 ViewRef с для компонентов, которые больше не отображаются в DOM для повышения производительности прокрутки.

При обновлении отображаются новые границы @Input() s, они не обновляют свое внутреннее состояние, поэтому ранее кэшированные копии в результате используются повторно.

Кажется, единственное решение - установить templateCacheSize: 0:

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

Таким образом, компоненты уничтожаются, когда они больше не видны, а состояние теряется.

Дальнейшее чтение https://github.com/angular/material2/issues/15838

...