開発覚書はてな版

個人的な開発関連の備忘録

【Angular】Virtual Scrollingの実装

概要

Angular CDK 7.0.0で Virtual Scrollingが追加されました。
これは表示されている部分のみDOM要素を生成するものになります。
表示以外の箇所のDOM生成は行わないため、パフォーマンスが向上します。

実行環境

  • Node.js 10.9.x

使用ライブラリ

  • Angular 7.0.x
  • Angular CDK 7.0.x

実装時の注意点

  • ScrollingModuleをimportして下さい。
  • <cdk-virtual-scroll-viewport>内の要素にcdkVirtualForを指定して下さい。
  • <cdk-virtual-scroll-viewport>itemSizeとスクロール上で表示する各行のheightは同じ値にする。(itemSizeで表示部分の高さ計算が行われるため)

サンプルソース

app.module.ts
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ScrollingModule } from '@angular/cdk/scrolling';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserAnimationsModule,
    FormsModule,
    ScrollingModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
app.component.html
<h1>Virtual Scrolling Sample</h1>
<div>
  List Count: <input type="number" [(ngModel)]="listSize">
</div>
<div style="margin-top: 10px;">
  <div style="display: inline-block; margin-right: 10px;">
    <div>
      Basic Scrolling
    </div>
    <div>
      <button (click)="setBasicList(listSize)">Apply</button>
      <button (click)="setBasicList(0)">Clear</button>
    </div>
    <div>
      <div style="width: 200px; height: 200px; border: 1px solid black; overflow-y: auto;">
        <div *ngFor="let item of basicList" style="height: 20px;">{{ item }}</div>
      </div>
    </div>
  </div>
  <div style="display: inline-block;">
    <div>
      Virtual Scrolling
    </div>
    <div>
      <button (click)="setVirtualList(listSize)">Apply</button>
      <button (click)="setVirtualList(0)">Clear</button>
    </div>
    <div>
      <cdk-virtual-scroll-viewport itemSize="20" style="width: 200px; height: 200px; border: 1px solid black;">
        <div *cdkVirtualFor="let item of virtualList" style="height: 20px;">{{ item }}</div>
      </cdk-virtual-scroll-viewport>
    </div>
  </div>
</div>
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  /** list item size */
  listSize = 100000;

  /** basic scroll list item */
  basicList: string[] = [];

  /** virtual scroll list item */
  virtualList: string[] = [];

  setBasicList(size: number): void {
    this.basicList = this.createList(size);
  }

  setVirtualList(size: number): void {
    this.virtualList = this.createList(size);
  }

  private createList(size: number): string[] {
    return Array.from({ length: size }).map((_, i) => `Item #${i}`);
  }
}
実行結果
Basic Scroll

https://user-images.githubusercontent.com/2668146/47263364-21b38500-d53b-11e8-8af4-d7deee0a2583.gif

Virtual Scroll

https://user-images.githubusercontent.com/2668146/47263370-3728af00-d53b-11e8-839a-a6d949f29366.gif

サンプルソース一式

github.com