Anguar Material CDK の Virtual Scrolling を触ってみた

#はじめに
この記事は [Angular Advent Calendar 2018](https://qiita.com/advent-calendar/2018/angular) 16日目の記事です。

Angular7がリリースされ、CDK に DragDropModule や ScrollingModule が追加されました。これらをインポートすると Drag and Drop や Virtual Scrolling が利用できます。
触ってみたかったのでサンプルに沿って少し触ってみました。

Drag and Drop に関しては [Angular Advent Calendar 2018 8日目の記事] (https://qiita.com/swfz/items/ac8daecad48d6dc948c0) で [@swfz](https://qiita.com/swfz) さんが書かれています！
本記事では　Virtual Scrolling を触ってみた件について書きます。

認識違い等ありましたらご指摘いただけるとありがたいです。

#まずv7にアップグレードする
ローカル環境がv６だったのでまずv7にあげました。

[こちら](https://update.angular.io/)を参照し該当する条件を選択
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103464/3ed6e718-619e-6923-ec93-9dc04d37e5de.png)


「Show me how to update!」クリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103464/b7185848-30ce-b6c5-9fca-ae623c44558f.png)
表示された手順に沿ってアップグレードします。

`ng update @angular/cli @angular/core`を実行したところ
`Could not find a package.json. Are you in a Node project?`と出たので、先に
`ng new [任意のプロジェクト名]`で新規プロジェクト作成後そのフォルダに移動して再度
`ng update @angular/cli @angular/core`を実行しました。

バージョンを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103464/2967d572-4b75-47d2-9440-c55a33a8891b.png)

v7になりました。

この方法だとプロジェクト単位のアップグレードになるので開発環境がまるっとv7になってOKであれば`npm install -g @angular/cli`を実行すればOKです。

上記手順を踏んだ後、`npm install -g @angular/cli`も試してみた所、先ほどのプロジェクト配下以外でもv7になったことが確認できました。この後はv7でプロジェクトを作成して進めていきます。

#新しくプロジェクトを作成
`ng new virtual-scrolling-sample`
で新規プロジェクトを作成します。
routingはなし、cssはscssを選択しました。

```ターミナル
$ ng new virtual-scrolling-sample
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? SCSS   [ http://sass-lang.com  
 ]
```
`ng serve -o`で起動します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103464/c02f7aee-f1dc-3b58-c3e1-a8af29cc540b.png)
起動できました。

#CDK を入れてみる
新規作成したプロジェクト配下で以下コマンドを実行します。（[参考](https://material.angular.io/guide/getting-started)）
`npm install --save @angular/cdk`

#サンプルを参考に Virtual Scrolling を入れてみる

ではサンプルを見つつVirtual Scrallを使ってみたいと思います。
一番シンプルそうなサンプルを参照。
https://material.angular.io/cdk/scrolling/overview#creating-items-in-the-viewport

まず、`src/app/app.module.ts`で`ScrollingModule `をインポートします。

```src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ScrollingModule } from '@angular/cdk/scrolling';

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

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, ScrollingModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

次に`src/app/app.component.ts`を修正します。
サンプルのコードのように
```items = Array.from({ length: 100000 }).map((_, i) => `Item #${i}`);```を追加してデータを用意します。

```src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  items = Array.from({ length: 100000 }).map((_, i) => `Item #${i}`);
}
```

htmlとcssもサンプルからコピーします。

```src/app/app.component.html
<cdk-virtual-scroll-viewport itemSize="50" class="example-viewport">
  <div *cdkVirtualFor="let item of items" class="example-item">{{ item }}</div>
</cdk-virtual-scroll-viewport>
```

```src/app/app.component.scss
.example-viewport {
  height: 200px;
  width: 200px;
  border: 1px solid black;
}

.example-item {
  height: 50px;
}
```

動いた:clap:

![scroll1.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/103464/88a3c57c-314e-9ab2-dbb6-392f81a34c08.gif)


簡単、サクサク。

#コンテキスト変数を使ってみる
`*cdkVirtualFor`では以下コンテキスト変数がサポートされています。（詳細は[ドキュメント](https://material.angular.io/cdk/scrolling/overview#virtual-scrolling)参照。）

| コンテキスト変数 | 説明 |
|:--|:--|
| index | データソース内のアイテムのインデックス |
| count | データソース内のアイテムの合計数 |
| first | データソースの最初の項目であるかどうか |
| last | データソースの最後の項目かどうか |
| even | indexが偶数であるかどうか |
| odd | indexが奇数であるかどうか |

以下のように`let even = even;`,`let odd = odd"`のように書くことでテンプレートで使えるようになります。
奇数行、偶数行で表示する画像を変えるようにしてみました。

```src/app/app.component.html
<cdk-virtual-scroll-viewport itemSize="150" class="example-viewport">
  <div
    *cdkVirtualFor="let item of items; 
                    let even = even; 
                    let odd = odd"
                    class="example-item">
    <p>{{ item }}</p>
    <ng-container *ngIf="even">
      <img src="../assets/img/me.jpg" />
    </ng-container>
    <ng-container *ngIf="odd">
      <img src="../assets/img/nise.jpg" />
    </ng-container>
  </div>
</cdk-virtual-scroll-viewport>
```

cssも少し調整。

```src/app/app.component.scss

.example-viewport {
  height: 500px;
  width: 200px;
  border: 1px solid black;
}

.example-item {
  height: 150px;
}

img {
  width: 100px;
}
```

奇数行、偶数行で画像が変わりました:clap:
![scroll2.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/103464/a1a4cc22-e765-c3f5-28ba-8e6743dcf204.gif)



# まとめ
実は Angular Material を触ったことがなかったのでこれを機に触れて良かったです。
試してみるのはとっても簡単でした！他の機能も触って見ようと思います。

明日のAngular Advent Calendar 2018 担当は [@ynishimura0922](https://qiita.com/ynishimura0922) さんです！