igx-grid の列定義の方法さまざま

*2018/12/07(Fri) 時点の情報に基づいています*

Ignite UI for Angular の `igx-grid` を表示させるのに最低限必要な情報が2つあります。

1. データ
2. 列定義

データは以下のような配列を用意します。

```typescript
[
  { id: 1, column1: 1, column2: 2, ... },
  { id: 2, column1: 1, column2: 2, ... },
  ...
]
```
上記のデータに対して、

- `id` に対する定義
- `column1` に対する定義
- `column2` に対する定義
- ・・・

とそれぞれどのように列を表示するかを定義する必要があります。列定義の方法にはいくつかあるので、それぞれ紹介していきます。

## 1. データから自動生成
まずは一番簡単な方法から。列定義を自動生成する方法です。
[前回の記事](https://qiita.com/tkiryu/items/d285fa025ebceef31d77) でも `igx-grid` を表示するのにこの方法を使いましたが、**`[autoGenerate]="true"`** を指定するだけです。

```html:xxx.component.html
  <!-- 
    こんな感じのデータを用意
    data = [
      { id: 1, column1: 1, column2: 2, ... },
      { id: 2, column1: 1, column2: 2, ... },
    ]
   このデータをバインドして [autoGenerate]="true" とすると列定義が自動生成されます
  -->
  <igx-grid [data]="data" [autoGenerate]="true"></igx-grid>
```

- **Pros**
  - データを用意するだけでよいです。
- **Cons**
  - `igx-grid` の機能が限られます。
      - 使える機能：フィルタリング、ページング、行選択、列非表示、列固定、エクスポート
      - 使えない機能：編集、列ソート、列移動、列リサイズ、列サマリー、複数列ヘッダー
  - 列のカスタマイズができません。例えば幅の指定など、自動生成のため全てが一律で同じに決まってしまいます。また、自分で列定義を記述すればテンプレートを使ってセル内の構造を自由にカスタマイズすることもできますが、これも自動生成してしまうとできなくなります。
- **使い所**
  - デモを作るときに**最小限の労力で**さくっと表示したいときに使います。
  - Web API からデータを引っ張ってきて、取得データが想定通りか**デバッグ**したいときに使ったりします。

## 2. `igx-column` を使った方法
1. の Cons を解消する方法です。  
**`igx-column`** というコンポーネントを使って、列定義を書いていきます。

```html:xxx.component.html
  <!-- 自分で列定義を書きます。その場合は autoGenerate を削除するか [autoGenerate]="false" とします -->
  <igx-grid [data]="data">
    <igx-column [field]="'id'" [header]="'ID'" [dataType]="'number'"></igx-column>
    <igx-column [field]="'column1'" [header]="'Column 1'" [dataType]="'string'"　[width]="200"></igx-column>
    <igx-column [field]="'column2'" [header]="'Column 2'" [dataType]="'string'"　[width]="300"></igx-column>
    ...
  </igx-grid>
```
列定義の基本プロパティは以下の3つです。受け付ける値は全て **`string型`** であることに注意してください。

- `[field]="'<列のキー>'"`
- `[header]="'<列の名前>'"`
- `[dataType]="'<列の型>'"` ※ string/number/boolean/date のみをサポートします

上記3つに加えて `[width]="200"` などとして個別に幅を指定しています。他にも [APIリファレンス](https://www.infragistics.com/products/ignite-ui-angular/docs/typescript/classes/igxcolumncomponent.html) を見ると、編集、列ソート、列移動、列リサイズ、列サマリーなどの機能を有効にするためのプロパティがあります。

- **Pros**
  - 1. の Cons を解消します。
- **Cons**
  - 列数が多くなってくると辛くなります。
- **使い所**
  - 表示データが自明で、列数がそれほど多くない場合に使います。
  - 往々にして列数は多くなりがちなので、実はあまり使い所がないかもしれません。

## 3. `igx-column` と `ngFor` を使った方法
2. の Cons を解消する方法です。
`igx-column` は `ngFor` と組み合わせて使うことができ、以下のような実装になります。これなら列数がいくつあっても対応できます。

```html:xxx.component.html
  <igx-grid [data]="data">
    <igx-column *ngFor="let column of columns"
                [field]="column.field"
                [header]="column.header"
                [dataType]="column.dataType"
                [width]="column.width">
    </igx-column>
  </igx-grid>
```
2 の方法とは違い、`igx-column` を `ngFor` で扱うために `columns` というコレクションを用意する必要が出てきます。以下は `Object.entries()` を使ってデータから key-value のペアを取得し `columns` を生成するサンプルです。

```typescript:xxx.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-xxx',
  templateUrl: './xxx.component.html',
  styleUrls: ['./xxx.component.scss']
})
export class XxxComponent implements OnInit {
  data: any[];

  columns: any[];

  ngOnInit(): void {
    // データの生成
    this.data = Array.from({ length: 1000 }).map((_, i) => {
      const num = i + 1;
      const item = { id: num };
      Array.from({ length: 99 }).forEach((__, j) => {
        let value;
        // string
        if (j % 4 === 0) value = `item${num}`;
        // number
        if (j % 4 === 1) value = num;
        // boolean
        if (j % 4 === 2) value = num % 2 === 0;
        // date
        if (j % 4 === 3) value = new Date(new Date().setDate(num));
        item[`column${j + 1}`] = value;
      });
      return item;
    });

    // 列定義の生成
    this.columns = Object.entries(this.data[0]).map(([key, value], index) => {
      const dataType = Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
      return {
        field: key,
        header: key.toUpperCase(),
        dataType,
        width: 100 + (index % 5) * 50
      };
    });
  }
}
```
TypeScript 側で `columns` のコレクションを作るためにいろいろやらなければなりませんが、非常に柔軟に列を定義することができるようになります。条件によって、A列は編集可、B列は編集不可、といった制御も可能です。

- **Pros**
  - 列数が増えても大丈夫です。
  - かなり柔軟に列定義を行うことができます。
- **Cons**
  - 列定義のコレクションを生成するのが複雑になりがちです。
- **使い所**
  - いろいろな状況で使え、**最も実用的**です。

## 4. 2 + 3 の複合技
**2 と 3 は組み合わせることができます。**
例えば、『必ず表示する列が2列あり、それ以外の列は可変としたい』といった要件があった場合に使えます。

```html:xxx.component.html
  <igx-grid [data]="data">
    <!-- 必ず表示するので 2 の方法で定義 -->
    <igx-column [field]="'foo'" [header]="'FOO'" [dataType]="'string'"></igx-column>
    <igx-column [field]="'bar'" [header]="'BAR'" [dataType]="'string'"></igx-column>

    <!-- 列数は状況によって増減するので 3 の方法で定義 -->
    <igx-column *ngFor="let column of columns"
                [field]="column.field"
                [header]="column.header"
                [dataType]="column.dataType"
                [editable]="column.editable">
    </igx-column>
  </igx-grid>
```
## 個人的な結論
- **『3. `igx-column` と `ngFor` を使った方法』** が**最も実用的**です。
- **『1. データから自動生成』** は、デモやデバッグ目的で使うことが多いです。ただし、使えないというわけではありません。データを表示するだけの要件にはマッチします。
- **『2. `igx-column` を使った方法』** 単品では**実はあまり出番がない**かもしれません。**『4. 2 + 3 の複合技』** で紹介したように、3と組み合わせることでより効果を発揮します。


## Ignite UI for Angular が気になった方はこちら
デモサイトで様々なサンプルを試すことができるので色々試してみてください！
https://jp.infragistics.com/products/ignite-ui-angular/angular/components/grid_virtualization.html