Angular × Wijmo はじめの一歩 FlexGrid 編 (Ver. 2021J v2)

こんにちは。

システムアーキテクト兼フロントエンド担当のたーせるです。

最近「どうしてもシングルページアプリケーションにデータグリッドを組み込みたい」という UI 要件が持ち上がったため、Angular と Wijmoウィジモ の組み合わせを採用しようと企んでいます。

f:id:tercel_s:20210911213648p:plain:w400
データグリッド

このブログでもときどき取り上げていますが、Wijmo はデータグリッドやグラフ、果ては地図まで、非常に高機能で複雑なコンポーネントがひととおり揃った有償の UI ライブラリです。

特に業務アプリケーションの開発では、発注元から「Excelのような操作性」を要求されることがあり、個人的にはデータグリッドを利用するだけでも Wijmo を採用する価値があると考えています。

ただ、一部の開発メンバからは「過去に、開発中のアプリケーションに Wijmo を組み込もうとして苦労したことがある」という声も聞こえてきました。

よくよく話を聞くと、そもそも Wijmo の正しい使い方が分からず、結局ようでぐちゃぐちゃな実装をしてしまった ── という苦い経験がトラウマになっている模様。

Wijmo 組み込み基本動作のおさらい

そこで今日は、初めての人でも安心して Wijmo に触れるよう、FlexGrid の基本のキホンを手を動かして学んでみようと思います。

細かい話はさておき、この記事を読み終える頃には Angular アプリケーションにベーシックなデータグリッドを組み込めるようになりますし、簡単なカスタマイズ要件であれば対応できるようになります。

特に、itemFormatter は、セルの外観をカスタマイズするための重要なテクニックの一つですので、ぜひ覚えておきたいところです*1

検証環境

  • Angular 12.2.5
  • Wijmo 2021J v2 (5.20212.812)

準備

こちらの公式記事の手順にならって、Wijmo をセットアップしていきます。 2019年と少し古い記事ですが、2021現在でも有効な手順でした。

devlog.grapecity.co.jp

Angular プロジェクトの作成

Angular のプロジェクト作成方法は既知であるものとし、特に詳しい説明は行いません。

叩いたコマンドだけ掲載しておきます。

$ ng new quickstart --routing --style css
$ cd quickstart
Wijmo のインストール

ターミナルに npm install コマンドを入力し、Wijmo をインストールします。

$ npm install @grapecity/wijmo.angular2.all

これで、試用版の Wijmo が使えるようになりました。

試用版であっても、製品版と同じようにコンポーネントを使うことができますが、実際にブラウザで動かした際に「Wijmo トライアル版」というクレジットが表示されます。

購入後にライセンスキーを設定することで、「トライアル版」の表示を消すことができます。

style.css の設定

引き続き、公式の手順に従ってチュートリアルを進めましょう。

Angular プロジェクト全体に共通で適用される style.css に、Wijmo のデフォルト CSS を読み込みます。

style.css

@import '@grapecity/wijmo.styles/wijmo.css';

恐らくここまでは、Angular で Wijmo を使ういかなるケースにおいても実施するであろう共通の手順です。

FlexGrid 初めの一歩

ここからは、Wijmo の中でも特に利用頻度の高いデータグリッドコンポーネント・ FlexGrid を Angular で利用するための手順になります。

テキストエディタapp.module.ts を開き、WjGridModule とカルチャをインポートしましょう。

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
// 略
import { WjGridModule } from '@grapecity/wijmo.angular2.grid'; // 追加
import '@grapecity/wijmo.cultures/wijmo.culture.ja';           // 追加

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    // 略
    WjGridModule // 追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


これで、FlexGrid を利用するための準備が完了したので、コンポーネントに FlexGrid を組み込んでグリッドを表示させてみましょう。

まず HTML 側は、<wj-flex-grid> タグでグリッド全体を作り、その子要素に、表示したいデータ項目の <wj-flex-grid-column> を並べていきます。

app.component.html

<wj-flex-grid [itemsSource]="gridData">
  <wj-flex-grid-column header="ID" binding="id" [width]="60">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="商品名" binding="product" [width]="200">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="受注日" binding="date" [width]="120">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="金額" binding="amount" [width]="100" format="c">
  </wj-flex-grid-column>
</wj-flex-grid>


続いて、グリッドにバインドするデータを TypeScript 側に記述します。

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  gridData = [
    { id: 15, product: 'ピュアデミグラスソース', date: '2017/01/10', amount: 6000 },
    { id: 17, product: 'だしこんぶ', date: '2017/01/08', amount: 14500 },
    { id: 18, product: 'ピリカラタバスコ', date: '2017/01/12', amount: 4000 },
    { id: 84, product: 'なまわさび', date: '2017/01/21', amount: 8000 }
  ];
}

ここまでは、公式チュートリアルとほぼ同じ手順です。

ng serve コマンドを叩くと、データグリッドが表示されることが確認できます。

実行結果
f:id:tercel_s:20210911205858p:plain

各項目の見出しを中央揃えにする

これで最低限の簡素なグリッドを画面に表示するところまではできました。

しかしよく見ると、各項目の見出しが右寄せだったり左寄せだったりと一貫しておらず、見た目が気になる方もいるかと思います。

そこで、itemFormatter を使って見出しを中央に揃えましょう。

f:id:tercel_s:20210911214347p:plainf:id:tercel_s:20210911214355p:plain
ビフォーアフター

app.component.html

<wj-flex-grid [itemsSource]="gridData"
              [itemFormatter]="itemFormatter">
  <wj-flex-grid-column header="ID" binding="id" [width]="60">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="商品名" binding="product" [width]="200">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="受注日" binding="date" [width]="120">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="金額" binding="amount" [width]="100" format="c">
  </wj-flex-grid-column>
</wj-flex-grid>


app.component.ts

import { Component } from '@angular/core';
import { CellType, GridPanel } from '@grapecity/wijmo.grid';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  gridData = [
    { id: 15, product: 'ピュアデミグラスソース', date: '2017/01/10', amount: 6000 },
    { id: 17, product: 'だしこんぶ', date: '2017/01/08', amount: 14500 },
    { id: 18, product: 'ピリカラタバスコ', date: '2017/01/12', amount: 4000 },
    { id: 84, product: 'なまわさび', date: '2017/01/21', amount: 8000 }
  ];
  itemFormatter = (panel: GridPanel, r: number, c: number, cell: HTMLElement) => {
    if (panel.cellType === CellType.ColumnHeader) {
      cell.style.textAlign = 'center';
    }
  }
}


itemFormatter を設定することで、セルの描画をカスタマイズできます。

引数の panel は GridPanel 型のデータで、これを通して当該セルを包含している FlexGrid のあらゆるデータや属性にアクセスできます。

r, c は、それぞれ描画対象のセルのrowcolumnインデックスを表しており、cell は描画対象のセルの HTML 要素そのものです。

ここでは、これから描画しようとしているセルの型が列ヘッダColumnHeaderの場合に限って、cell に中央揃えのスタイルを適用するコードを書いています。

以前も書きましたが、itemFormatter は、少なくとも可視状態のセルすべてに対して逐一呼び出されるため、あまりコストの高い処理を書いてしまうとレンダリング性能に影響が出てしまいます。

実行結果
f:id:tercel_s:20210911210159p:plain

見出しが全て中央揃えになりました。

itemFormatter の引数はアノテーションをしっかり書くのがポイントだよ。

ふむふむ。

手を抜いて any とかにしちゃうと、後々悪い方に効いてくるから……。

ぉぉぅ……。


カーソルを変える

続いて、FlexGrid 上のカーソル形状を Excel っぽく変えてみましょう。

f:id:tercel_s:20210911214355p:plainf:id:tercel_s:20210911214819p:plain
ビフォーアフター

HTML テンプレートに一行 [showMarquee]="true" を追加するだけです。

app.component.html

<wj-flex-grid [itemsSource]="gridData"
              [showMarquee]="true"
              [itemFormatter]="itemFormatter">
  <wj-flex-grid-column header="ID" binding="id" [width]="60">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="商品名" binding="product" [width]="200">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="受注日" binding="date" [width]="120">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="金額" binding="amount" [width]="100" format="c">
  </wj-flex-grid-column>
</wj-flex-grid>

実行結果
f:id:tercel_s:20210911211507p:plain


行ヘッダを非表示にする

画面の幅が狭いデバイスで表示している場合や、表示項目が多い場合など、スペースを節約するためにグリッド左端の行ヘッダを非表示にしたいケースがあります。

この場合は、HTML テンプレートに headersVisibility="Column" を設定すると、Columnヘッダのみが表示されるようになります。

app.component.html

<wj-flex-grid [itemsSource]="gridData"
              [showMarquee]="true"
              [itemFormatter]="itemFormatter"
              headersVisibility="Column">
  <wj-flex-grid-column header="ID" binding="id" [width]="60">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="商品名" binding="product" [width]="200">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="受注日" binding="date" [width]="120">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="金額" binding="amount" [width]="100" format="c">
  </wj-flex-grid-column>
</wj-flex-grid>

実行結果
f:id:tercel_s:20210911211021p:plain

なお、headersVisibility には Column の他に、以下の定数が指定できます。

  • All(デフォルト: 行ヘッダも列ヘッダも表示する)
  • Row(行ヘッダのみを表示し、列ヘッダが非表示になる)
  • None(行ヘッダも列ヘッダも非表示になる)

フィルタを追加する

最後は、データを絞り込むためのフィルタを追加してみましょう。

見出しにろうのようなアイコンが表示され、レコードを条件で絞り込むことができるようになります。

f:id:tercel_s:20210911214616p:plainf:id:tercel_s:20210911215135p:plain
ビフォーアフター

ここで新しいモジュールをインポートします。 app.module.tsWjGridFilterModule を追加しましょう。

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
// 略
import { WjGridModule } from '@grapecity/wijmo.angular2.grid';
import { WjGridFilterModule } from '@grapecity/wijmo.angular2.grid.filter'; // 追加
import '@grapecity/wijmo.cultures/wijmo.culture.ja';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    // 略
    WjGridModule,
    WjGridFilterModule // 追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


続いてHTML側の修正です。<wj-flex-grid> の直下に <wj-flex-grid-filter> タグを追加するだけです。

app.component.html

<wj-flex-grid [itemsSource]="gridData"
              [showMarquee]="true"
              [itemFormatter]="itemFormatter"
              headersVisibility="Column">

  <wj-flex-grid-filter></wj-flex-grid-filter>

  <wj-flex-grid-column header="ID" binding="id" [width]="60">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="商品名" binding="product" [width]="200">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="受注日" binding="date" [width]="120">
  </wj-flex-grid-column>
  <wj-flex-grid-column header="金額" binding="amount" [width]="100" format="c">
  </wj-flex-grid-column>
</wj-flex-grid>

実行結果
f:id:tercel_s:20210911212350p:plain
見出しの部分に、フィルタ用のアイコンが表示されました。

このアイコンをクリックすると、フィルタを掛けるためのダイアログが表示されます。
f:id:tercel_s:20210911212208p:plain

まとめ

今回は、Wijmo を使ったグリッド組み込みの基本のキホンについて学びました。

次なる壁はパフォーマンスとの戦いになるのですが、まずはライブラリの正しい使い方を開発メンバ全員が正しく知ってスタートを切れることが大事かなーと思いました。

ちなみにパフォーマンスで困ることとその対策についてはこちら。

tercel-tech.hatenablog.com

*1:同様の手法として、formatItem や、よりパフォーマンスに優れた CustomCellFactory といった別の手段がありますが、最も簡便な手段はやはり itemFormatter になります。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.