Data Grid

Ignite UI for Angular Data Grid は、表形式でデータの表示や編集ができます。最小限のコードと構成でデータをすばやくバインドできます。フィルタリング、並べ替え、ページング、テンプレート、移動列、データの編集と更新機能をサポートします。ユーザー操作が簡単に認識できるため、コードで制御できます。

デモ


依存関係

グリッドが NgModule としてエクスポートされるため、アプリケーションで AppModuleIgxGridModule をインポートする必要があります。

// app.module.ts

import { IgxGridModule } from 'igniteui-angular';
// Or
import { IgxGridModule } from 'igniteui-angular/grid';

@NgModule({
    imports: [
        ...
        IgxGridModule.forRoot(),
        ...
    ]
})
export class AppModule {}

IgxGridModule の各コンポーネント、ディレクティブ、およびヘルパー クラスはグリッド サブパッケージまたは igniteui-angular のメイン バンドルでインポートできます。グリッドをインスタンス化して使用するためにすべての機能をインポートする必要はありませんが、グリッド API の一部である型を宣言する場合はインポート (またはエディターで自動的にインポート) します。

import { IgxGridComponent } from 'igniteui-angular/grid/';
// Or
import { IgxGridComponent } from 'igniteui-angular'
...

@ViewChild('myGrid', { read: IgxGridComponent })
public grid: IgxGridComponent;

使用方法

グリッド モジュールをインポート後、ローカル データにバインドする igx-grid の基本構成を設定します。

<igx-grid #grid1 id="grid1" [data]="localData" [autoGenerate]="true"></igx-grid>

id プロパティは文字列値で、設定されない場合に自動生成されるグリッドの一意識別子です。data はグリッドをローカル データにバインドします。

autoGenerate プロパティは、データソース フィールドに基づいて igx-grid にグリッドのIgxColumnComponent を自動生成させます。列の適切なデータ型の決定を試みます。それ以外の場合、開発者は列およびデータ ソース フィールドへのマッピングを明示的に定義する必要があります。

スタイルの構成

Note

IgxGridComponentcss グリッド レイアウトを使用しますが、プレフィックスなしでは IE でサポートされていないため、正しく描画できません。

Angular のほとんどのスタイルは Autoprefixer プラグインで暗示的にプレフィックスされてます。

ただし、グリッド レイアウトのプレフィックスでは、Autoprefixer グリッド プロパティ をコメント /* autoprefixer grid:on */ で有効にする必要があります。

作業を容易にするためにコメントを src/styles.scss ファイルに適用します。

// src/styles.scss
   @import '~igniteui-angular/lib/core/styles/themes/index';
   @include igx-core();
   @include igx-theme($default-palette);

   /* autoprefixer grid:on */
...

列の構成

IgxColumnComponent は、グリッドの columns コレクションを定義し、フィルタリング並べ替えページングなど、列ごとの機能を有効にするために使用します。セル、ヘッダー、およびフッター テンプレートも利用できます。

autoGenerate プロパティを無効にし、マークアップで列コレクションを定義します。

<igx-grid #grid1 [data]="data | async" [autoGenerate]="false" [paging]="true" [perPage]="6" (onColumnInit)="initColumns($event)"
    (onSelection)="selectCell($event)" [allowFiltering]="true">
    <igx-column field="Name" [sortable]="true" header=" "></igx-column>
    <igx-column field="AthleteNumber" [sortable]="true" header="Athlete number" [filterable]="false"></igx-column>
    <igx-column field="TrackProgress" header="Track progress" [filterable]="false">
        <ng-template igxCell let-value>
            <igx-linear-bar [stripped]="false" [value]="value" [max]="100"></igx-linear-bar>
        </ng-template>
    </igx-column>
</igx-grid>

グリッドの各列は別のテンプレートを持つことができます。列にグリッド モジュール ディレクティブの 1 つでデコレートした ng-template タグが必要です。

igxHeader は列ヘッダーを対象とし、列オブジェクトをコンテキストとして提供します。

...
<igx-column field="Name">
    <ng-template igxHeader let-column>
        {{ column.field | uppercase }}
    </ng-template>
</igx-column>
...

igxCell は提供したテンプレートを列のすべてのセルに適用します。テンプレートで提供されるコンテキスト オブジェクトは暗示的に提供されたセル値およびセル オブジェクトです。以下のようにセルがコンテンツに応じて拡張するテンプレートを定義するために使用できます。

...
<igx-column field="Name">
    <ng-template igxCell let-value>
        {{ value | titlecase }}
    </ng-template>
</igx-column>
...

上記のスニペットで暗示的に提供されたセル値への参照を取得します。データを表示し、セルの値にカスタム スタイル設定およびパイプ変換を適用する場合に使用します。ただし、IgxGridCellComponent オブジェクトを以下のように使用するとより効果的です。

<igx-grid #grid [data]="data">
    <igx-column dataType="string" field="Name">
        <ng-template igxCell let-cell="cell">
            <!-- Implement row deleting inside the cell template itself -->
            <span tabindex="0" (keydown.delete)="grid.deleteRow(cell.rowIndex)">{{ cell.value | titlecase }}</span>
        </ng-template>
    </igx-column>
    <igx-column dataType="boolean" field="Subscribtion">
        <ng-template igxCell let-cell="cell">
            <!-- Bind the cell value through the ngModel directive and update the data source when the value is changed in the template -->
            <input type="checkbox" [ngModel]="cell.value" (ngModelChange)="cell.update($event)" />
        </ng-template>
    </igx-column>
<igx-grid>

列は、セルが編集モードにある場合に使用されるテンプレートを使用します。その他の列テンプレートと同じように、提供されるコンテキスト オブジェクトはセル値およびセル オブジェクトです。編集モード テンプレートをユーザー アクセス可能にするには、IgxColumnComponenteditable プロパティを true に設定します。

<igx-column dataType="number" editable="true" field="Price">
    <ng-template igxCellEditor let-cell="cell">
        <label for="price">
            Enter the new price tag
        </label>
        <input name="price" type="number" [ngModel]="cell.value" (ngModelChange)="cell.update(convertToNumber($event))" />
    </ng-template>
</igx-column>

テンプレートで使用可能なプロパティの詳細については、IgxGridCellComponent の API を参照してください。

各列テンプレートが IgxColumnComponent オブジェクトでコードによって変更可能です。以下のコード例で、ユーザー データの 2 つのテンプレートを宣言しました。TypeScript コードでテンプレートへの参照を取得し、条件に基づいてアプリケーションで列の適切なテンプレートを描画します。

<igx-grid>
    <!-- Column declarations -->
</igx-grid>

<ng-template #normalView let-value>
    <div class="user-details">{{ val }}</div>
    <user-details-component></user-details-component>
</ng-template>

<ng-template #smallView let-value>
    <div class="user-details-small">{{ val }}</div>
</ng-template>
@ViewChild("normalView", { read: TemplateRef })
public normalView: TemplateRef<any>;

@ViewChild("smallView", { read: TemplateRef })
public smallView: TemplateRef<any>;

....

const column = this.grid.getColumnByName("User");
// Return the appropriate template based on some conditiion.
// For example saved user settings, viewport size, etc.
column.bodyTemplate = this.smallView;

列プロパティもグリッドで列が初期化されるときに発生される initColumns イベントのコードで設定できます。

public initColumns(column: IgxGridColumn) {
    const column: IgxColumnComponent = column;
    if (column.field === 'ProductName') {
        column.sortable = true;
        column.editable = true;
    }
}

上記のコードは ProductName 列の並べ替えや編集機能を有効にし、対応する機能の UI (編集の入力など) をインスタンス化します。

データ構造

IgxGridComponentフラットデータのみ取得します。描画に固有のデータ構造はフォームにあります。

const OBJECT_ARRAY = [{
        ObjectKey1: value1,
        ObjectKey2: value2,
        .
        .
        .
        ObjectKeyN: valueN
    },
    {
        ObjectKey1: value1,
        ObjectKey2: value2,
        .
        .
        .
        ObjectKeyN: valueN
    },
    .
    .
    .,
    {
        ObjectKey1: value1,
        ObjectKey2: value2,
        .
        .
        .
        ObjectKeyN: valueN 
    }];
Warning

キー値に配列またはその他のオブジェクトを含まないでください。

autoGenerate 列を使用する場合、データキーが同一である必要があります。

データ バインディング

はじめにリモート データ サービスにバインドするためにグリッドを変更します。大規模なアプリケーション レベルでは一般的なシナリオです。すべてのデータ取得に関連するロジックを別のデータ サービスに分割することがベスト プラクティスであるため、サーバーからデータの取得を処理するサービスを作成します。

サービスを別のファイルで実装します。

// northwind.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { catchError, map } from 'rxjs/operators';

各 Angular サービス定義で必須要素 となる Injectable デコレータをインポートします。HttpClient はバックエンド サービスに接続する機能を提供します。グリッド コンポーネントにサブスクライブする結果である Observable を返します。

: Angular 5 の前では HttpClient@angular/http にあり、名前は Http でした。

レコードの配列を含む JSON レスポンスを受け取るため、監視可能な要素に返されるデータの型を指定するために適切なインターフェイスを定義します。タイプ チェックを行うことにより、後で発生する可能性のある問題を防止できます。

// northwind.service.ts

export interface NorthwindRecord {
    ProductID: number;
    ProductName: string;
    SupplierID: number;
    CategoryID: number;
    QuantityPerUnit: string;
    UnitPrice: number;
    UnitsInStock: number;
    UnitsOnOrder: number;
    ReorderLevel: number;
    Discontinued: boolean;
    CategoryName: string;
}

サービスは Observable<NorthwindRecord[]> を返す fetchData の単一のメソッドを含みます。要求が任意の理由 (サーバーが利用不可、ネットワーク エラーなど) により失敗した場合、HttpClient はエラーを返します。catchError 演算子を使用して失敗した Observable を傍受してエラーをエラー ハンドラーへ渡します。エラー ハンドラーはエラーをログして値を返します。

// northwind.service.ts

@Injectable()
export class NorthwindService {
    private url = 'http://services.odata.org/V4/Northwind/Northwind.svc/Alphabetical_list_of_products';

    constructor(private http: HttpClient) {}

    public fetchData(): Observable<NorthwindRecord[]> {
        return this.http
            .get(this.url)
            .pipe(
                map(response => response['value']),
                catchError(
                    this.errorHandler('Error loading northwind data', [])
                )
            );
    }

    private errorHandler<T>(message: string, result: T) {
        return (error: any): Observable<any> => {
            console.error(`${message}: ${error.message}`);
            return of(result as T);
        };
    }
}

HttpClientModule および作成したサービスをアプリケーションのモジュールにインポートし、サービスをプロバイダーとして登録します。

// app.module.ts

import { HttpClientModule } from '@angular/common/http';
...
import { NorthwindService } from './northwind.service';

@NgModule({
    imports: [
        ...
        HttpClientModule
        ...
    ],
    providers: [
        NorthwindService
    ]
})
export class AppModule {}

サービスを実装した後、コンポーネントのコンストラクターにインジェクトしてデータを取得するために使用します。ngOnInit ライフサイクル フックに最初の要求を追加します。

: 以下のコードでは、サービスに加入する前に records プロパティを空の配列に設定しています。Http 要求は非同期です。完了するまで records プロパティは undefined で、グリッドをプロパティにバインドするときにエラーが発生されます。デフォルト値に初期化、または BehaviorSubject を使用します。

// my.component.ts

@Component({
    ...
})
export class MyComponent implements OnInit {

    public records: NorthwindRecord[];

    constructor(private northwindService: NorthwindService) {}

    ngOnInit() {
        this.records = [];
        this.northwindService.fetchData().subscribe((records) => this.records = records);
    }
}

コンポーネントのテンプレートのコード:

    <igx-grid [data]="records">
        <igx-column field="ProductId"></igx-column>
        <!-- rest of the column definitions -->
        ...
    </igx-grid>

: リモート データにバインドする場合、グリッドの autoGenerate プロパティは使用しないことをお勧めします。データを検証して適切な列を生成するためにデータが利用可能である必要があります。リモート サービスの応答が完了するまでデータが利用できないため、グリッドはエラーを発生します。リモート サービスへバインド時に autoGenerate を使用する機能は今後追加予定です。

複雑なデータ バインディング

IgxGridComponent の主な目的はフラット データを処理することですが、これはより複雑なデータを扱うことが不可能であることを意味するものではありません。

現在、グリッド列は複合キーをサポートしていませんが、他の列から列を作成することができます。このセクションでは、ネスト データフラット データを使用して IgxGridComponent を構成する方法について説明します。

ネスト データrowData

以下は、階層データを IgxGrid へバインドする方法です。

  • ネストされたデータを含むセルの値
  • カスタム列テンプレート

以下は使用するデータです。

export const EMPLOYEE_DATA = [
    {
        Age: 55,
        Employees: [
            {
                Age: 43,
                HireDate: new Date(2011, 6, 3),
                ID: 3,
                Name: "Michael Burke",
                Title: "Senior Software Developer"
            },
            {
                Age: 29,
                HireDate: new Date(2009, 6, 19),
                ID: 2,
                Name: "Thomas Anderson",
                Title: "Senior Software Developer"
            },
            {
                Age: 31,
                HireDate: new Date(2014, 8, 18),
                ID: 11,
                Name: "Monica Reyes",
                Title: "Software Development Team Lead"
            },
            {
                Age: 35,
                HireDate: new Date(2015, 9, 17),
                ID: 6,
                Name: "Roland Mendel",
                Title: "Senior Software Developer"
            }],
        HireDate: new Date(2008, 3, 20),
        ID: 1,
        Name: "John Winchester",
        Title: "Development Manager"
    },
...

ネスト データをレンダリングする列のカスタム テンプレート。

...
 <igx-column field="Employees" header="Employees" [cellClasses]="{ expand: true }" width="40%">
        <ng-template #nestedDataTemp igxCell let-people let-cell="cell">
            <div class="employees-container">
                <igx-expansion-panel *ngFor="let person of people">
                    <igx-expansion-panel-header iconPosition="right">
                        <igx-expansion-panel-description>
                            {{ person.Name }}
                        </igx-expansion-panel-description>
                    </igx-expansion-panel-header>
                    <igx-expansion-panel-body>
                        <div class="description">
                            <igx-input-group (keydown)="stop($event)" displayDensity="compact">
                                <label igxLabel for="title">Title</label>
                                <input type="text" name="title" igxInput [(ngModel)]="person.Title" style="text-overflow: ellipsis;" />
                            </igx-input-group>
                            <igx-input-group (keydown)="stop($event)" displayDensity="compact" style="width: 15%;">
                                <label igxLabel for="age">Age</label>
                                <input type="number" name="age" igxInput [(ngModel)]="person.Age" />
                            </igx-input-group>
                        </div>
                    </igx-expansion-panel-body>
                </igx-expansion-panel>
            </div>
        </ng-template>
 </igx-column>
...

以下は、この設定の結果です。

フラット データ

フラットデータバインディングのアプローチは既に説明したものと似ていますが、セル値の代わりに、IgxRowComponentrowData プロパティを使用します。

グリッドはデータレコードをレンダリング操作保存するためのコンポーネントのため、すべてのデータ レコードへアクセスすることで、それを処理する方法をカスタマイズすることができます。それには、rowData プロパティを使用します。

以下は使用するデータです。

export const DATA: any[] = [
    {
        Address: "Obere Str. 57",
        City: "Berlin",
        CompanyName: "Alfreds Futterkiste",
        ContactName: "Maria Anders",
        ContactTitle: "Sales Representative",
        Country: "Germany",
        Fax: "030-0076545",
        ID: "ALFKI",
        Phone: "030-0074321",
        PostalCode: "12209",
        Region: null
    },
...

カスタム テンプレート:

...
<igx-column field="Address" header="Address" width="25%" editable="true">
                <ng-template #compositeTemp igxCell let-cell="cell">
                    <div class="address-container">
                    // In the Address column combine the Country, City and PostCode values of the corresponding data record 
                        <span><strong>Country:</strong> {{cell.row.rowData.Country}}</span>
                        <br/>
                        <span><strong>City:</strong> {{cell.row.rowData.City}}</span>
                        <br/>
                        <span><strong>Postal Code:</strong> {{cell.row.rowData.PostalCode}}</span>
                    </div>
                </ng-template>
...

上記で定義したテンプレートでは編集操作ができないため、エディター テンプレートが必要であることに注意してください。

...
                 <ng-template  igxCellEditor let-cell="cell">
                        <div class="address-container">
                        <span>
                            <strong>Country:</strong> {{cell.row.rowData.Country}}
                            <igx-input-group width="100%">
                                    <input igxInput [(ngModel)]="cell.row.rowData.Country" />
                            </igx-input-group>
                        </span>
                            <br/>
                            <span><strong>City:</strong> {{cell.row.rowData.City}}</span>
                            <igx-input-group width="100%">
                                    <input igxInput [(ngModel)]="cell.row.rowData.City" />
                            </igx-input-group>
                            <br/>
                            <span><strong>Postal Code:</strong> {{cell.row.rowData.PostalCode}}</span>
                            <igx-input-group width="100%">
                                    <input igxInput [(ngModel)]="cell.row.rowData.PostalCode" />
                            </igx-input-group>
                            <br/>
                        </div>
                </ng-template>
</igx-column>
...

以下は結果です。

パーシステンス (永続化) 状態

ページ/セッション間でグリッドの状態を維持することは一般的なシナリオであり、現在アプリケーション レベルで実現可能です。ページをまたいで状態のパーシステンスを実装します。この例では、localStorage オブジェクトを使用して状態の JSON 文字列を格納していますが、必要に応じて sessionStorage オブジェクトを使用することもできます。実装の詳細はすべて igxState ディレクティブに抽出されます。

// state.directive.ts

@Directive({
    selector: "[igxState]"
})
export class IgxGridStateDirective {

    public ngOnInit() {
        this.loadGridState();
        this.router.events.pipe(take(1)).subscribe((event: NavigationStart) => {
            this.saveGridState();
        });
    }

    public ngAfterViewInit() {
        this.restoreGridState();
    }

    public saveGridState() { ... }
    public loadGridState() { ... }
    public restoreGridState() { ... }
}

上の例にあるように、NavigationStart イベントが発生すると (ユーザーがページから移動するたびに) saveGridState メソッドが呼び出されます。このメソッドには、グリッドの状態 (ソートおよびフィルター式、ページング状態、列の順序など) を読み込むロジックが含まれ、選択された行のコレクション)を作成して、このデータを json 文字列として localStorge に保存します。後でユーザーがグリッドに戻ったときに、loadGridStaterestoreGridState メソッドがそれぞれ OnInitAfterViewInitライフサイクル フック中に呼び出されます。 loadGridState は JSON 文字列を localStorage から gridState オブジェクトにデコードします。一方、restoreGridState はgrid APIを使用して、対応する並べ替えとフィルタリングの式をグリッドに適用したり、ページングを設定したりします。

最後にディレクティブをグリッドに適用し、グリッド コンポーネントの OnInit フック間で列コレクションを復元します。

// grid.component.ts

public ngOnInit() {
    const columnsFromState = this.state.getColumnsForGrid(this.gridId);
    this.columns = this.state.columns && columnsFromState ?
        columnsFromState : this.initialColumns;
}

既知の問題と制限

制限 説明
percentage および px で設定した列幅 列に %px を組み合わせて使用することはできません。
number 型の列をフィルターする場合 フィルター入力に入力された値が number と異なる場合、キャストが正しくないため NaN が返されます。
グリッドの width が列幅に依存しない すべての列の width でグリッド自体のスパンは決定しません。親コンテナーのディメンションまたは定義したグリッドの width で決定されます。
親コンテナーでネストされた Grid グリッドの width を設定せずに定義済みのディメンションで親コンテナーに配置した場合、グリッドがコンテナーに合わせてスパンします。
Grid OnPush ChangeDetectionStrategy グリッドで ChangeDetectionStrategy.OnPush を処理し、カスタム表示されたときにグリッドに発生した変更について通知します。
列には設定可能な最小幅があります。displayDensity オプションに基づきます。
"compact": 24px
"cosy": 32px
"comfortable ": 48px
許容される最小幅未満に設定した場合、描画要素には影響せずに対応する displayDensity に合わせて許容される最小幅で描画します。水平方向の仮想化は予期しない動作を招く場合があるためサポートしていません。
ビューに描画されていないセル高さは行の高さに影響しません。 仮想化のため、セルの高さを変更するビューにないカスタム テンプレートの列は行の高さに影響しません。関連する列がビューにスクロールされるときのみ行の高さに影響します。

API リファレンス

その他のリソース

コミュニティに参加して新しいアイデアをご提案ください。