Angular Material 7 で Virtual Scrolling を試してみる
Angular6.1で遊んでいたらAngular7がリリースされたので、Virtual Scrollingでちょっと遊んでみる。
1.Angular CLIのアップデート
まずは・・・先日Angular CLIを6.2.5へアップデートしたばかりだが、Angular CLIも7.0.2がリリースされていたのでアップデートする。
$ npm uninstall -g @angular/cli $ npm cache verify $ npm install -g @angular/cli@7.0.2
念のためバージョンを確認する。
$ ng version _ _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ Angular CLI: 7.0.2 Node: 8.12.0 OS: win32 x64 Angular: ... Package Version ------------------------------------------------------ @angular-devkit/architect 0.10.2 @angular-devkit/core 7.0.2 @angular-devkit/schematics 7.0.2 @schematics/angular 7.0.2 @schematics/update 0.10.2 rxjs 6.3.3 typescript 3.1.3
2.Angular7でプロジェクトを新規作成する
Angular CLI 7.X で ng new
を実行すると、routingの使用やスタイルシートのフォーマットについて聞かれる。
$ ng new virtualscrolling ? Would you like to add Angular routing? No ? Which stylesheet format would you like to use? SCSS [ http://sass-lang.com ] CREATE virtualscrolling/angular.json (3949 bytes) CREATE virtualscrolling/package.json (1323 bytes) CREATE virtualscrolling/README.md (1033 bytes) ...
プロジェクトができたところで、Angularのバージョンを確認してみる。
$ cd virtualscrolling $ ng version ... Angular CLI: 7.0.2 Node: 8.12.0 OS: win32 x64 Angular: 7.0.0 ... animations, common, compiler, compiler-cli, core, forms ... http, language-service, platform-browser ... platform-browser-dynamic, router ...
無事 Angular 7.0.0 がインストールされた。
3.Angular Materialインストール
次は Angular Material 7.X をインストールする。
$ npm install --save @angular/material @angular/cdk @angular/animations ... + @angular/animations@7.0.0 + @angular/material@7.0.0 + @angular/cdk@7.0.0 ...
インストールが終わったら、公式サイトの「Getting started | Angular Material」に従い、
Step 2: Configure animations Step 4: Include a theme
を作成したプロジェクトに適用しておく(詳細は割愛)。他の手順は必要に応じて後でやる。さて、ここまでで一旦動かしてみる。
$ ng serve --open
問題なく「Welcome to virtualscrolling!」が表示される。
3.データの準備
とりあえず、そこそこの量のデータを・・・ということで、app.component.ts の codes 配列に、Unicode文字を0x0000 ~ 0x9999の約4万文字用意する。
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { title = 'virtualscrolling'; codes = []; ngOnInit() { for (let i = 0x0000; i < 0x9999; i++) { this.codes.push({ code: '0x' + ('0000' + i.toString(16).toLowerCase()).substr(-4), char: String.fromCharCode(i) }); } } }
4.Virtual Scrolling
まずは、app.component.html を以下のようにして、約4万文字をそのままループで表示してみる。
<div *ngFor="let i of codes"> {{i.code}} - {{i.char}} </div>
表示にかかる時間を計測すると、約9秒程度。
次に、Virtual Scrollingを試してみる。app.module.ts へ ScrollDispatchModule を追加して、
import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ScrollDispatchModule } from '@angular/cdk/scrolling'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, BrowserAnimationsModule, ScrollDispatchModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
app.component.scss を(本家のサンプルをそのままに)以下とし、
.example-viewport { height: 200px; width: 200px; border: 1px solid black; } .example-item { height: 50px; }
app.component.html を(こちらも本家のサンプルを参考に)以下とする。
<cdk-virtual-scroll-viewport itemSize="50" class="example-viewport"> <div *cdkVirtualFor="let i of codes" class="example-item">{{i.code}} - {{i.char}}</div> </cdk-virtual-scroll-viewport>
表示されるまでの時間は1秒以下(厳密には計測してないが、そのほとんどはAngularの初期化にかかる時間だろう)。DOMの状態をDevToolsで確認してみると、
<cdk-virtual-scroll-viewport _ngcontent-c0="" class="example-viewport cdk-virtual-scroll-viewport cdk-virtual-scroll-orientation-vertical" itemsize="50" ng-reflect-item-size="50"> <div class="cdk-virtual-scroll-content-wrapper" style="transform: translateY(896950px);"> <!--bindings={ "ng-reflect-cdk-virtual-for-of": "[object Object],[object Object" }--> <div _ngcontent-c0="" class="example-item">0x4613 - 䘓</div> <div _ngcontent-c0="" class="example-item">0x4614 - 䘔</div> <div _ngcontent-c0="" class="example-item">0x4615 - 䘕</div> <div _ngcontent-c0="" class="example-item">0x4616 - 䘖</div> <div _ngcontent-c0="" class="example-item">0x4617 - 䘗</div> <div _ngcontent-c0="" class="example-item">0x4618 - 䘘</div> <div _ngcontent-c0="" class="example-item">0x4619 - 䘙</div> <div _ngcontent-c0="" class="example-item">0x461a - 䘚</div> <div _ngcontent-c0="" class="example-item">0x461b - 䘛</div> <div _ngcontent-c0="" class="example-item">0x461c - 䘜</div> <div _ngcontent-c0="" class="example-item">0x461d - 䘝</div> </div> <div class="cdk-virtual-scroll-spacer" style="transform: scaleY(1.96605e+06);"></div> </cdk-virtual-scroll-viewport>
スクロールバーの操作に応じて、div.cdk-virtual-scroll-content-wrapper
の内容が一定量を超えないよう動的に変化する動きをしていた。また、スクロールバーを正しく表示させるために、div.cdk-virtual-scroll-content-wrapper
の translateY と、div.cdk-virtual-scroll-spacer
の scaleY を変化させて表示行の上下に巨大な空白を作り出していた・・・なるほど、そういう仕組みなのね!