Virtual Scroll コンポーネント
Ignite UI for Angular Virtual Scroll は、ビューポート内のアイテムと設定可能なバッファーのみを DOM 内に保持することで、大量のリストをレンダリングするコンポーネントです。スクロールバーは常にコレクション全体の範囲を表すため、10 万件のアイテムを持つ仮想リストでも通常のリストと同じようにスクロールできます。
ライブ デモ
構造
Angular Virtual Scroll は、表示されているアイテムと設定可能なバッファーをレンダリングし、そのトラックはコレクション全体のスクロール範囲を保持します。
2. トラック: コレクション全体の推定される長さに合わせてサイズが設定されるスペーサーです。これにより、スクロールバーがすべてのアイテムに及びます。
3. コンテンツ要素: レンダリングされたアイテムのみを保持します。ビューポートの上にある、最初にレンダリングされたバッファー アイテムから始まり、そのサイズはレンダリングされたアイテムによって決まります。
4. アイテム ラッパー: レンダリングされたアイテムごとに 1 つ存在し、アイテム テンプレートをホストします。サイズが測定されるのはこのボックスです。
5. オーバー スキャン バッファー: ビューポートの各端を超えてレンダリングされる
overScan 個のアイテム (デフォルトは 2) です。
igx-virtual-scroll — scrollable viewport (role="list")
└── .igx-virtual-scroll__track — provides the collection's scroll range
└── .igx-virtual-scroll__content — positions the rendered window
└── .igx-virtual-item — one wrapper per rendered item (data-index); hosts the item template
作業の開始
作業の開始 のトピックに従って Ignite UI for Angular をセットアップし、VirtualScroll と、アイテム テンプレートをマークする IgxVirtualItemDirective をインポートします。
import { Component } from '@angular/core';
import { IgxVirtualItemDirective, IgxVirtualScrollComponent } from 'igniteui-angular/virtual-scroll';
@Component({
selector: 'app-employees',
imports: [IgxVirtualScrollComponent, IgxVirtualItemDirective],
templateUrl: './employees.component.html'
})
export class EmployeesComponent {
public items = Array.from({ length: 100_000 }, (_, i) => ({ name: `Item ${i}` }));
}
<igx-virtual-scroll [data]="items" style="height: 400px">
<ng-template igxVirtualItem let-item let-index="index">
<div class="row">{{ index }}: {{ item.name }}</div>
</ng-template>
</igx-virtual-scroll>
Virtual Scroll のホストには、垂直スクロールの場合は固定の高さ、水平スクロールの場合は固定の幅が必要です。コンテンツに合わせてサイズが拡大するホストはすべてのアイテムをレンダリングするため、リストは仮想化されません。
前提条件とバージョン互換性
| 要件 | 値 |
|---|---|
| パッケージ | igniteui-angular (MIT) |
| エントリ ポイント | igniteui-angular/virtual-scroll |
| コンポーネントが最初にリリースされたバージョン | 22.2.0 |
使用方法
アイテム テンプレート
Virtual Scroll のアイテム テンプレートは、アイテムとコレクション全体におけるその位置を受け取ります。位置に依存するコンテンツ (交互のスタイルや aria-posinset、aria-setsize など) には、インデックスと合計件数を使用します。
ng-template に igxVirtualItem を付与するか、itemTemplate を通じて他の場所で定義されたテンプレートを渡します (こちらが優先されます)。テンプレートのコンテキストは $implicit (アイテム)、index、count、first、last、even、odd を提供します。
<igx-list>
<igx-virtual-scroll role="presentation" [data]="employees" [estimatedItemSize]="64" style="height: 480px">
<ng-template igxVirtualItem let-employee let-index="index" let-count="count">
<igx-list-item [attr.aria-posinset]="index + 1" [attr.aria-setsize]="count">
<igx-avatar igxListThumbnail shape="circle" [initials]="employee.initials"></igx-avatar>
<span igxListLineTitle>{{ employee.name }}</span>
<span igxListLineSubTitle>{{ employee.email }}</span>
</igx-list-item>
</ng-template>
</igx-virtual-scroll>
</igx-list>
データ
Virtual Scroll の data コレクションは参照によって比較されます。リストを更新するには新しい配列を割り当てる必要があります。push などでバインドされた配列をその場で変更しても更新されません。
this.employees = [...this.employees, newEmployee];
data が変更されると、コンポーネントは最初に変更されたインデックスより前のアイテムの測定済みサイズを保持し、それ以降のアイテムはレンダリング時に再度測定します。追加操作ではすべての既存の測定値が保持されますが、置換、フィルタリング、ソートでは最初に変更されたアイテム以降の測定値が破棄されます。
推定アイテム サイズ
Virtual Scroll の estimatedItemSize は、アイテムがレンダリングされて測定されるまでのピクセル単位のサイズです (デフォルトは 50)。アイテムは異なるサイズを持つことができ、測定されたサイズがそれぞれ推定値を置き換えます。
アイテムが測定される前にスクロールバーと scrollToIndex を正確に保つため、推定値はアイテムの平均サイズに近い値に設定してください。
<igx-virtual-scroll [data]="employees" [estimatedItemSize]="80" style="height: 480px">...</igx-virtual-scroll>
アイテムはボーダー ボックスで測定されるため、マージンはアイテムのサイズに含まれません。マージンの代わりに、パディング、またはアイテム内の gap を使用してアイテム間の間隔を設定してください。
方向
Virtual Scroll の orientation は、スクロール軸を vertical (デフォルト) または horizontal に設定します。水平リストでは、各アイテムに幅を、ホストに高さを設定してください。右から左のコンテキストでは、水平スクロールとアイテムの配置が反転します。
<igx-virtual-scroll orientation="horizontal" [data]="employees" [estimatedItemSize]="220" style="height: 200px">
<ng-template igxVirtualItem let-employee>
<div style="width: 220px">...</div>
</ng-template>
</igx-virtual-scroll>
オーバー スキャン
Virtual Scroll の overScan は、ビューポートの各端を超えてレンダリングされる追加アイテムの数です (デフォルトは 2)。値を大きくすると、高速スクロール中の空白領域が減りますが、レンダリングする要素が増えます。
<igx-virtual-scroll [data]="items" [overScan]="6" style="height: 400px">...</igx-virtual-scroll>
インデックスへのスクロール
Virtual Scroll の scrollToIndex メソッドは、アイテムが表示される位置までスクロールします。オプションはネイティブの scrollIntoView と同じで、block (start、center、end、または nearest)、水平リスト用の inline、behavior (auto または smooth) を指定できます。まだレンダリングされていないアイテムには推定サイズしかないため、コンポーネントは到達した位置のアイテムを測定して位置を補正します。返されるプロミスは最終的な位置が確定すると解決されます。
private readonly virtualScroll = viewChild.required(IgxVirtualScrollComponent);
public async goTo(index: number): Promise<void> {
await this.virtualScroll().scrollToIndex(index, { block: 'center' });
}
block: 'nearest' を指定した場合、アイテムがすでに完全に表示されているときは位置が変更されません。コレクションの範囲外のインデックスは、最初または最後のアイテムに丸められます。
無限スクロール
Virtual Scroll の dataRequest 出力は、リモート データからの追加専用の読み込みをサポートします。これは、レンダリングされたウィンドウが data の末尾に近づいたときや、読み込まれたアイテムが最初のレンダリングでビューポートを満たさない場合に発生します。要求されたアイテムを新しい配列として追加します。
<igx-virtual-scroll [data]="employees()" [estimatedItemSize]="64" (dataRequest)="loadMore($event)" style="height: 440px">
<ng-template igxVirtualItem let-employee>...</ng-template>
</igx-virtual-scroll>
public readonly employees = signal<Employee[]>(firstPage);
public loadMore(request: VirtualScrollDataRequest): void {
this.service.fetch(request.startIndex, request.count).subscribe(page => {
this.employees.update(current => [...current, ...page]);
});
}
一度に保留できるデータ要求は 1 つだけで、次の要求は data が次に変更された後に発行されます。data が空の場合は要求が発行されないため、最初のページは自分で読み込む必要があります。ソースにこれ以上アイテムがない場合は、追加を停止するだけでかまいません。コンポーネントが同じ開始インデックスを再度要求することはありません。
ページ分割されたデータ
Angular Virtual Scroll の dataWindow 入力は、data の代わりに大きなコレクションの 1 ページをバインドします。リストは totalCount と同じ長さになるため、ページのみがメモリ上にある間もスクロールバーはコレクション全体の範囲を表し、ページがカバーしていないインデックスは何もレンダリングしません。
interface VirtualDataWindow<T> {
readonly items: readonly T[]; // the loaded page
readonly startIndex: number; // the index of items[0] in the whole collection
readonly totalCount: number; // the size of the whole collection
}
stateChange が報告する範囲から次のページを読み込みます。遅い応答が新しいページを上書きしないように、前の要求をキャンセルします。
<igx-virtual-scroll [dataWindow]="page()" [estimatedItemSize]="64" (stateChange)="onStateChange($event)" style="height: 440px">
<ng-template igxVirtualItem let-employee>...</ng-template>
</igx-virtual-scroll>
public readonly page = signal<VirtualDataWindow<Employee>>({ items: [], startIndex: 0, totalCount: 100_000 });
private pending?: Subscription;
public onStateChange(state: VirtualScrollState): void {
const page = this.page();
if (state.startIndex >= page.startIndex && state.endIndex < page.startIndex + page.items.length) {
return; // The loaded page already covers the range.
}
const startIndex = Math.max(0, state.startIndex - 30);
const count = state.endIndex + 30 - startIndex + 1;
this.pending?.unsubscribe();
this.pending = this.service.fetch(startIndex, count).subscribe(result => {
this.page.set({ items: result.items, startIndex, totalCount: result.total });
});
}
totalCount が変わらない間は、インデックスごとに測定済みサイズが保持されます。フィルタリング結果のように totalCount が異なるページは再度測定されます。dataWindow がバインドされている間は dataRequest は発生しません。コンポーネントはインデックスごとに 1 つのサイズ エントリを保存するため、そのメモリは totalCount に応じて増加します。100 万件のアイテムでおおよそ 17 MB です。
レイアウト完了
Virtual Scroll の layoutComplete プロパティは、現在のレンダリング、それによってトリガーされる測定、そしてそれらがスケジュールするレンダリングが完了したときに解決されるプロミスです。data の変更、スクロール、リサイズの後にレンダリングされたアイテムを読み取る前に、これを待機してください。
this.employees = await firstValueFrom(this.service.fetchAll());
await this.virtualScroll().layoutComplete;
使用すべき場合と使用すべきでない場合
Virtual Scroll は通常、長いリストのスクロール コンテナーとして機能し、ビューポート内のアイテムと小さなバッファーのみを DOM に保持します。一度にレンダリングできる短いリストには使用しないでください。また、アイテム テンプレートを作成する際は、各アイテムの状態を要素ではなくデータに保持してください。アイテムの要素は再利用されるため、テンプレートがバインドしていない DOM の状態は、その要素を受け取ったアイテムに表示されます。

ディレクトリ、フィード、ログ、横一列に並んだカードなど、一度にレンダリングするには大きすぎる長いリストに対して Virtual Scroll を使用します。これには、スクロール中にリモート データを読み込むリストも含まれます。

短いリストは List と @for で直接レンダリングします。列、ソート、フィルタリングを伴う表形式のデータには Angular Data Grid を使用します。仮想化せずに少数のリッチなアイテムを表示するには Card を使用します。
プロパティ
| 名前 | 型 | デフォルト | 説明 |
|---|---|---|---|
data |
T[] |
[] |
仮想化するコレクション。参照によって比較されます。 |
dataWindow |
VirtualDataWindow<T> | null |
null |
設定されている間、data の代わりに使用される、より大きなコレクションの 1 ページ。 |
orientation |
'vertical' | 'horizontal' |
'vertical' |
スクロール軸。 |
overScan |
number |
2 |
ビューポートの各端を超えてレンダリングされる追加アイテムの数。 |
estimatedItemSize |
number |
50 |
アイテムが測定されるまでのピクセル単位のサイズ。0 以下の値の場合は 50 が使用されます。 |
itemTemplate |
TemplateRef<IgxVsItemContext<T>> | null |
null |
アイテム テンプレート。投影された ng-template[igxVirtualItem] より優先されます。 |
layoutComplete |
Promise<void> (読み取り専用) |
— | レンダリングとアイテムの測定が完了すると解決されます。 |
メソッド
| 名前 | 戻り値 | 説明 |
|---|---|---|
scrollToIndex(index: number, options?: ScrollIntoViewOptions) |
Promise<void> |
index のアイテムをビューにスクロールし、最終的なスクロール位置が確定すると解決されます。 |
イベント
| 名前 | ペイロード | 説明 |
|---|---|---|
stateChange |
VirtualScrollState |
レンダリングされたウィンドウが変更されたときに発生します: startIndex、endIndex、viewportSize、totalSize。 |
dataRequest |
VirtualScrollDataRequest |
レンダリングされたウィンドウが data の末尾に近づいたときに発生します: startIndex、count。dataWindow がバインドされている間は発生しません。 |
スタイル設定
Angular Virtual Scroll には独自のテーマはありません。ビューポートのレイアウトのみを行い、レンダリングされたアイテムはアイテム テンプレート内の要素やコンポーネントからスタイルを継承します。
ホストのサイズを設定し、構造 のクラスを使用してレンダリングされたアイテムを対象にします。
.employees igx-virtual-scroll {
block-size: 480px;
}
.employees .igx-virtual-item:nth-child(even) {
background: var(--ig-gray-100);
}
アクセシビリティ
Angular Virtual Scroll は、レンダリングされたウィンドウのみを DOM に保持するため、アイテム テンプレートはコレクション全体の中での各アイテムの位置を公開する必要があります。
キーボード インタラクション
Angular Virtual Scroll はキー ハンドラーを追加しません。ホストはネイティブのスクロール コンテナーであり、フォーカスされたスクロール コンテナーはブラウザー標準のキー操作でスクロールします。
| キー | アクション |
|---|---|
| 上矢印 / 下矢印 | 垂直リストをスクロールします。 |
| 左矢印 / 右矢印 | 水平リストをスクロールします。 |
| Page Up / Page Down | ビューポート 1 つ分ほどスクロールします。 |
| Home / End | コレクションの先頭または末尾にスクロールします。 |
ホストには tabindex がありません。フォーカス可能なコンテンツがないスクロール コンテナーがフォーカスを受け取れるかどうかはブラウザーによって異なるため、アイテムにフォーカス可能な要素が含まれない場合は、ホストに tabindex="0" を設定してください。アイテム内のフォーカスは、そのアイテムがレンダリングされたウィンドウから外れると保持されません。外れる前に意図的にフォーカスを移動してください。
スクリーン リーダー / ARIA
- ホストには
role="list"が設定されています。トラック、コンテンツ要素、アイテム ラッパーにはrole="presentation"が設定されています。igx-list-itemのようにrole="listitem"をレンダリングするアイテムは、そのリストのアイテムとして公開されます。 igx-listのように既にリストのセマンティクスを提供するコンテナー内では、アイテムが二重のリストに入れ子にならないよう、ホストにrole="presentation"を設定してください。indexとcountのテンプレート変数をaria-posinsetとaria-setsizeにマップします。- フォーカス可能なホストには、
aria-labelまたはaria-labelledbyでアクセシブルな名前を付けてください。
アクセシビリティ準拠
インフラジスティックスは、Ignite UI for Angular が対象とするアクセシビリティ標準を アクセシビリティ準拠 トピックで文書化しています。このトピックは Virtual Scroll に対する準拠の主張を行うものではありません。表にはコンポーネントが提供する内容が記載されており、その後のリストにはアプリケーションが追加する必要がある内容が記載されています。
| 基準 | コンポーネントが要件をサポートする方法 |
|---|---|
| 1.3.1 情報及び関係性 | ラッパーには role="presentation" が設定されているため、リスト構造はホストとアイテム テンプレートから提供され、aria-posinset と aria-setsize で各アイテムの位置を公開できます。 |
| 2.1.1 キーボード | ホストはネイティブのスクロール コンテナーであり、フォーカスを得るとキーボードでスクロールできます。キーボードでホストに到達できるかどうかはアプリケーションに依存します。以下のリストを参照してください。 |
ユーザー側の責任:
- アイテムにフォーカス可能な要素が含まれない場合は、
tabindex="0"を設定してホストをキーボードで到達可能にし、アクセシブルな名前を付けてください。 - アイテム テンプレートから
aria-posinsetとaria-setsizeでアイテムの位置を公開してください。 - アイテム テンプレートに適したリスト セマンティクスを提供してください (スクリーン リーダー / ARIA を参照)。
- 選択などのアプリケーションの状態は、レンダリングされたアイテム要素ではなくデータ内に保持してください。
トラブルシューティング
Virtual Scroll がアイテムをレンダリングしないのはなぜですか?
ホストにスクロール軸方向のサイズがない、アイテム テンプレートがない、または data が空です。ホストに固定の高さ (垂直) または幅 (水平) を設定し、アイテム テンプレートを設定して、バインドされたコレクションを確認してください。
アイテムを追加してもリストが更新されないのはなぜですか?
Virtual Scroll は data を参照によって比較するため、その場での変更は検出されません。[...items, newItem] のように新しい配列を割り当ててください。
スクロール中にスクロールバーのサイズが変わるのはなぜですか?
まだレンダリングされていないアイテムは estimatedItemSize を使用しており、アイテムが測定されるにつれて合計サイズが補正されます。
estimatedItemSize をアイテムの平均サイズに近い値に設定してください。
リストの下の方でアイテムの位置がずれるのはなぜですか?
マージンはアイテムの測定済みサイズに含まれません。アイテムのマージンをパディングまたはアイテム内の gap に置き換えてください。
ドロップダウンやダイアログの中のリストのアイテムが 1 フレーム遅れて表示されるのはなぜですか?
開くまで非表示になっているコンテナーは、それを表示する変更検出パスの時点ではサイズを持たないため、ホストはそのレンダリングの後に測定され、アイテムは次のフレームでレンダリングされます。レンダリングされたアイテムを読み取るのは layoutComplete の解決後に行ってください。
igxForOf リストを Virtual Scroll に置き換えるにはどうすればよいですか?
Virtual Scroll は実行時にアイテムを測定し、独自のスクロール コンテナーを作成するため、igxForOf のコンテナー サイズおよびスクロール コンテナーの入力には相当するものがありません。igxForOf ディレクティブは非推奨となり、代わりに Virtual Scroll の使用が推奨されます。既存のリストは引き続き動作しますが、単一の軸を仮想化する新しいリストには Virtual Scroll を使用してください。
| igxForOf | Virtual Scroll |
|---|---|
*igxFor="let item of data" |
ng-template igxVirtualItem を伴う [data]="data" |
igxForScrollOrientation |
orientation |
igxForContainerSize |
CSS で設定するホストの高さまたは幅 |
igxForItemSize |
estimatedItemSize (開始時の推定値。アイテムは測定されます) |
igxForScrollContainer |
不要: ホストがスクロール コンテナーです |
scrollTo(index) |
プロミスを返す scrollToIndex(index, options) |
chunkLoad、chunkPreload |
stateChange |
リモート データ向けの igxForTotalItemCount |
totalCount を伴う dataWindow、または追加専用の読み込み向けに dataRequest を伴う data |
index、count、first、last、even、odd |
同じテンプレート変数 |
グリッドには独自の行と列の仮想化機能があります。グリッドの仮想化 を参照してください。
既知の制限
-
Angular Virtual Scroll は単一の軸を仮想化します。行と列の両方を仮想化するにはグリッドが必要です。
-
ウィンドウの移動に伴ってアイテムの要素は再利用されるため、
checkedをバインドしていないチェックボックスなど、アイテム テンプレートがバインドしていない DOM の状態は、その要素を受け取ったアイテムに表示されます。アイテムの状態はすべてバインドし、ユーザーによる変更はアイテムに書き戻してください。 -
dataWindowを使用する場合、totalCountは維持されるが同じインデックスに異なるレコードが配置されるページでは、それらの行が再度レンダリングされるまで以前のレコードの測定済みサイズが保持されます。
API リファレンス
依存関係
Angular Virtual Scroll は、他のコンポーネントへの依存関係を持ちません。igniteui-angular/virtual-scroll から IgxVirtualScrollComponent と IgxVirtualItemDirective をインポートしてください。構造スタイルはコンポーネントに同梱されています。
その他のリソース
関連コンポーネント
- List - 短いリストや、仮想化されたリストのコンテナーとして List を使用します。
- Data Grid - 列、ソート、フィルタリングを伴う表形式のデータには Data Grid を使用します。
- Card - 少数のリッチなアイテムの表示や、水平方向の Virtual Scroll のアイテムとしてカードを使用します。
- Virtual ForOf ディレクティブ - 既存のリストで使用されるディレクティブ ベースの仮想化。
FAQ
Angular Virtual Scroll は、ビューポート内のアイテムとオーバー スキャン バッファーのみを DOM に保持するため、コレクションのサイズによってレンダリングされる要素の数は変わりません。コレクションの合計サイズがブラウザーのスクロール制限を超える場合、Virtual Scroll はコレクションをブラウザーがサポートするスクロール範囲にマッピングします。
Angular Virtual Scroll のアイテムは、レンダリングされた時点でそれぞれが測定されるため、異なるサイズにすることができます。アイテムが測定される前にスクロールバーを正確に保つため、estimatedItemSize をアイテムの平均サイズに近い値に設定してください。
Angular Virtual Scroll の scrollToIndex メソッドを、アイテムのインデックスと、省略可能な block および behavior オプションを指定して呼び出します。このメソッドは、補正された位置が安定すると解決されるプロミスを返します。
Angular Virtual Scroll は 2 つのモデルをサポートします。追加専用の読み込みでは dataRequest を処理し、要求されたアイテムを含む新しい配列を割り当てます。コレクションを一度に 1 ページずつ読み込む場合は、dataWindow をバインドし、stateChange が報告する範囲を読み込みます。