はじめに
Wijmo(ウィジモ)は、グレープシティがHTML/JavaScript環境に向けて提供しているJavaScriptライブラリで、WebサイトやWebアプリケーションで活用できるUI部品を利用者に提供します。現行版はECMAScript 5に対応した「Wijmo 5」です。
Wijmoは単体での利用に加え、さまざまなJavaScriptフレームワークやライブラリと組み合わせて利用できます。本連載で主に紹介してきたAngularのほか、ReactやVue.js、Knockoutをサポートします。今回は、最近注目が高まっているReactでWijmoを利用する方法を紹介します。
対象読者
- WebサイトやWebアプリケーションのレベルをワンランク上げたい方
- AngularでWijmoを使っていて、最近話題のReactも試したい方
- 高度なUI部品を手軽に使いたいReactユーザーの方
必要な環境
Wijmo 5はECMAScript 5に対応するブラウザーをサポートします。詳細はWijmoのWebサイトで案内されています。
Reactでは、プロジェクトを生成する「create-react-app」ツールが利用できます。ツールの動作にはNode.jsが必要です。
以上を踏まえて、今回は以下の環境で動作を確認しています。
-
Windows 10 64bit版
- Wijmo 5 5.20182.524
- React 16.5.2
- Node.js v8.12.0 64bit版
- Microsoft Edge 42.17134.1.0
サンプルコードを実行するには、プロジェクトのフォルダーで「npm install」コマンドを実行してライブラリをダウンロード後、「npm start」コマンドを実行します。
Reactとは
Reactは、Facebook社とオープンソースコミュニティで開発されているJavaScriptライブラリで、以下の特徴があります。
Viewのみを担当
Reactは画面のユーザーインターフェース(View)作成にフォーカスしたライブラリで、それ以外の機能を提供する任意のライブラリと組み合わせて利用できます。
仮想DOM
Webページ要素のツリー構造をメモリー上で操作して、最小限の差分だけを実際のWebページ(DOMツリー)に反映する仕組みで、高速な動作を実現します。
単一方向のデータフロー
Reactでは、保持しているデータを画面に反映する一方で、画面の変更を自動的にデータに反映する機能(いわゆる「双方向データバインディング」)は提供しません。データの流れを単一方向にすることで、わかりやすいコードを実現します。
Reactの詳細については、CodeZineの連載記事「基礎からはじめるReact入門」も参考にしてください。
Reactプロジェクトを生成して内容を確認
ReactでWijmoを利用する前準備として、ここでは、create-react-appツールで生成するReactのプロジェクトについて説明します。Node.jsがインストールされている環境で、リスト1の通りコマンドを実行すると、Reactのプロジェクトを生成して実行できます。(1)のnpxは、create-react-appをインストールせずに、直接実行できるコマンドです。
npx create-react-app p001-default # プロジェクト生成 ...(1) cd p001-default # プロジェクトフォルダーに移動 ...(2) npm start # プロジェクト実行 ...(3)
リスト1(3)を実行するとWebブラウザーが起動して、図1のように表示されます。
本記事では、リスト1のコマンドで生成したReactプロジェクトを修正して、WijmoのコントロールをReactプロジェクトで表示させています。Reactプロジェクト構成の詳細については、CodeZineのReact記事も参考にしてください。
ReactプロジェクトにWijmoを追加して動作させる
ReactプロジェクトでWijmoを利用できるようにするには、リスト2のコマンドを実行します。Node.jsのパッケージマネージャーnpmを利用して、プロジェクトにWijmoを追加できます。
npm install --save wijmo
プロジェクトに追加したWijmoの利用方法を、図2のサンプルで説明します。このサンプルでは、Wijmoのゲージコントロール(LinearGauge)と、ゲージの値を設定するテキストボックスを画面に表示します。マウスやタッチでゲージを操作すると、テキストボックスの数値が更新されます。逆に、テキストボックスの数値を変更すると、ゲージが更新されます。
最初に、画面表示に対応するAppコンポーネント(App.js)にリスト3の通り記述して、Wijmoのファイルやモジュールをインポートします。(1)はWijmoのCSS、(2)は日本語リソースです。(3)は、このサンプルで利用するゲージ機能を含むWijmoのモジュール(wijmo.react.gauge)を「wjGauge」という名前でインポートする記述です。
import 'wijmo/styles/wijmo.css'; // CSS ...(1) import 'wijmo/cultures/wijmo.culture.ja'; // 日本語リソース ...(2) import * as wjGauge from 'wijmo/wijmo.react.gauge'; // Wijmoモジュール ...(3)
次に、Appクラスを、リスト4の通り実装します。
class App extends Component { // コンストラクター // ...(1) constructor(props) { // スーパークラスのコンストラクターでpropsを初期設定 // ...(2) super(props); // stateを初期設定 ...(3) this.state = { gaugeValue: 30 } // メソッドをthisにバインド ... (4) this.gaugeChanged = this.gaugeChanged.bind(this); this.textboxChanged = this.textboxChanged.bind(this); } // コンポーネントの表示 ...(5) render() { return ( <div className="App"> <h1>Wijmo + React(ゲージ)</h1> {/* WijmoのLinearGauge ...(6) */} <wjGauge.LinearGauge className="wijmo-control" isReadOnly={false} value={this.state.gaugeValue} valueChanged={this.gaugeChanged}/> {/* ゲージの値を表示・設定するテキストボックス ...(7)*/} ゲージの値:<input type="text" className="textbox" value={this.state.gaugeValue} onChange={this.textboxChanged}/> </div> ); } // ゲージが変更されたときの処理 ...(8) gaugeChanged(newValue) { this.setState({gaugeValue: newValue.value}); } // テキストボックスが変更されたときの処理 ...(9) textboxChanged(newValue) { this.setState({gaugeValue: Number(newValue.target.value)}); } }
(1)はクラスのコンストラクターです。Reactのコンポーネントでは、コンストラクターの引数で渡される情報のオブジェクト(props)を、スーパークラスのコンストラクターで(2)のように初期設定します。また、Reactコンポーネントの内部状態を管理するthis.stateプロパティを、(3)で初期設定します。ここではゲージ値の初期値30をgaugeValueに設定します。(4)は、後述するイベント処理内で、Appクラス自身をthisとして参照できるようにする処理です。
(5)のrenderは、コンポーネントを表示するReactのメソッドで、HTMLタグによく似た記法でコンポーネントの表示内容を記述できます。この記法はJSXと呼ばれるJavaScript拡張記法です。
(6)がWijmoのゲージ(LinearGauge)に対応する記述で、表1の属性を設定します。「value={this.state.gaugeValue}」のようにタグの属性値を{}で囲むのはReactの記法で、JavaScriptの変数やメソッドを設定できます。
属性名 | 属性の意味 | 設定内容 |
---|---|---|
isReadOnly | 読み取り専用かどうか | false(操作可能) |
value | ゲージの値 | this.state.gaugeValue(stateに保持した値) |
valueChanged | ゲージ変更時の処理 | リスト4(8)のthis.gaugeChangedメソッド |
同様に、テキストボックスに対応する記述(7)には、表2の属性を設定します。
属性名 | 属性の意味 | 設定内容 |
---|---|---|
value | テキストボックスの値 | this.state.gaugeValue(stateに保持した値) |
onChange | テキストボックス変更時の処理 | リスト4(9)のthis.textboxChangedメソッド |
ゲージの変更時は(8)、テキストボックスの変更時は(9)の処理が実行されます。引数から変更後の値を取得して、this.setStateメソッドでstateのgaugeValueを更新しています。this.setStateは、stateを更新するReactのメソッドで、更新内容は画面に自動的に反映されます。
[注]単一方向のデータフロー
Reactでは、データフローが単一方向のため、画面が更新されても自動的に内部状態(state)が更新されません。画面が更新された場合、リスト4(8)(9)のようにthis.setStateメソッドで明示的にstateを更新する必要があります。
対してAngularでは、双方向データバインディング機能で、画面と内部状態を双方向に同期します。図2と類似のサンプルをAngularで実装した本連載の過去記事も参照してください。
グリッド部品FlexGridとチャート部品FlexChartを試す
WijmoのFlexGridはExcelのようなグリッドを表示する部品、FlexChartはさまざまなチャートを表示する部品です。これらの利用例を、図3のサンプルで説明します。グリッドの内容を変更すると、連動してチャートに反映されます。
まず、利用するWijmoのモジュールを、リスト5の通りインポートします。(1)はFlexGrid、(2)はFlexChartに対応します。(3)はWijmo本体のモジュールで、FlexGridやFlexChartに表示するデータを管理するCollectionViewクラスが含まれます。
import * as wjGrid from 'wijmo/wijmo.react.grid'; // FlexGrid ...(1) import * as wjChart from 'wijmo/wijmo.react.chart'; // FlexChart ...(2) import * as wj from 'wijmo/wijmo'; // Wijmo本体(CollectionView) ...(3)
Appクラスのコンストラクターでは、FlexGridとFlexChartで共有するデータをstateに設定します。ここでは、ym(年月)/time(稼働時間)/count(生産数)のデータを持つJavaScriptオブジェクトの配列をコンストラクターに与えてCollectionViewを生成し、cvDataとしてstateに設定します。
this.state = { cvData: new wj.CollectionView([ { 'ym': '2018/9', 'time': 160, 'count': 200 }, (略) ]) };
renderメソッドで、画面に表示するFlexGridとFlexChartを記述します。FlexGridの記述はリスト7です。
<wjGrid.FlexGrid itemsSource={this.state.cvData}> ...(1) <wjGrid.FlexGridColumn header="年月" binding="ym" width="*"/> ...(2) <wjGrid.FlexGridColumn header="稼働時間" binding="time" width="*"/> ...(3) <wjGrid.FlexGridColumn header="生産数" binding="count" width="*"/> ...(4) </wjGrid.FlexGrid>
(1)のFlexGridタグがグリッドの本体で、表示するデータ(this.state.cvData)をitemsSource属性に設定します。(2)~(4)のFlexGridColumnタグはグリッドの列で、binding属性でym(年月)、time(稼働時間)、count(生産数)をそれぞれ表示するようにします。「width="*"」は、列を可能な限り広げて表示する設定です。
FlexChartはリスト8の通り記述します。
<wjChart.FlexChart itemsSource={this.state.cvData} bindingX="ym"> ...(1) <wjChart.FlexChartSeries name="稼働時間" binding="time"/> ...(2) <wjChart.FlexChartSeries name="生産数" binding="count" chartType="LineSymbols"/> ...(3) <wjChart.FlexChartLegend position="Bottom"/> ...(4) </wjChart.FlexChart>
(1)のFlexChartタグがチャートの本体で、itemsSource属性にはリスト7同様に表示データを設定します。bindingX属性はチャートのx軸設定で、ここではym(年月)をx軸に設定します。(2)と(3)のFlexChartSeriesタグはグラフのデータ系列で、それぞれのbinding属性で(2)にtime(稼働時間)、(3)にcount(生産数)を表示するよう設定します。(3)ではグラフ種類を表すchartType属性にLineSymbols(線グラフ)を設定します。chartType属性を指定しない(2)のデータ系列は棒グラフになります。(4)のFlexChartLegendタグは凡例で、position属性で表示位置をBottom(下部)に設定します。
CollectionViewを利用したデータ同期と画面の更新
リスト5~8のサンプル(p003-chart-grid)には、リスト3~4のサンプル(p002-basic)のように、画面更新時にstateを更新する実装がありませんが、FlexGridの変更がFlexChartに反映されます。これはCollectionViewの機能によるもので、CollectionViewを共有するWijmoコントロール間では、データが自動的に同期されて画面に反映されます。
一方、Wijmoコントロール以外の画面要素とデータを共有する場合は、stateを更新して画面に反映するReactの方法に従う必要があります。このような実装方法を、FlexGridの内容をHTMLのtableタグで表示する図4のサンプルで説明します。
FlexGridの記述はリスト7と同様です。データを表示するテーブルは、リスト9の通り記述します。(1)でCollectionView(this.state.cvData)のitemsプロパティでデータを取得して、mapメソッドで各行を<tr>タグ、<td>タグとして出力します。
<table className="disp-table"> <th>年月</th> <th>稼働時間</th> <th>生産数</th> { // CollectionViewのデータを出力 ...(1) this.state.cvData.items.map((value, index, array) => { return( <tr> <td>{value.ym}</td> <td>{value.time}</td> <td>{value.count}</td> </tr>) }) } </table>
このままではCollectionViewの変更がテーブルに反映されないので、CollectionViewが変更されたときに発生するonCollectionChangedイベントの処理を、リスト10の通り実装します。this.setStateメソッドでCollectionViewを再設定すると、Reactの内部処理でテーブルが再描画されます。
this.state.cvData.onCollectionChanged = (() => { this.setState({cvData: this.state.cvData}); });
[注]ライセンスの設定
npmでインストールしたWijmoは正式版ですが、ライセンスキーを設定しないとトライアル版として動作し、画面上にトライアル版である旨が表示されます。公式ページの手順で取得したライセンスキーを、リスト11のようにsetLicenseKeyメソッドで設定すると、Wijmoが正式版として動作するようになり、トライアル版の表示が消えます。
import * as wj from 'wijmo/wijmo'; wj.setLicenseKey('<ここにライセンスキーを設定>');
まとめ
本記事では、グレープシティのJavaScriptライブラリWijmo 5を、Reactと組み合わせて利用する方法を説明しました。ReactプロジェクトにWijmoを組み込むことで、WijmoのコントロールをReactのコンポーネントとしてシームレスに活用できます。