Wijmoを活用したダッシュボードの実装
ここまでで基本的な準備ができたので、以下でWijmoを活用してダッシュボードを実装していきます。
ライセンスキー設定とCollectionView
最初に、Wijmoにライセンスキーを設定する方法と、各部品で表示するデータを管理するCollectionViewについて説明します。
// Wijmoライセンスキーの設定 ...(1) wijmo.setLicenseKey('<ライセンスキー>'); // 売上データのCollectionView ...(2) const salesDataCV = new wijmo.collections.CollectionView(salesData, { // 合計列を追加 ...(2a) calculatedFields: { 'sum': s => s.store1 + s.store2 + s.store3 } });
ライセンスキーは(1)のwijmo.setLicenseKeyメソッドの引数として設定します。ライセンスキーを設定しないとトライアル版として動作し、トライアル版である旨が画面に表示されます。ライセンスについての詳細は公式ページを参照してください。
(2)でCollectionViewを定義します。CollectionViewは、元となるJavaScript配列内のデータに追加の処理を行えるWijmoのクラスです。wijmo.collections.CollectionViewコンストラクターの第1引数にリスト2(2)のJavaScript配列salesDataを、第2引数にはオプションを指定します。このサンプルでは(2a)のcalculatedFieldsオプションで、store1~store3の値を合計したsumフィールド(全店舗の売上合計に対応)を定義しています。CollectionViewの詳細は公式ドキュメントを参照してください。
FlexGridによるグリッド表示
FlexGridで売上データをグリッド表示する方法を説明します。HTML側にはリスト4の通り、FlexGridを表示するHTML要素を記述しておきます。
<div class="grid" id="grid-elem"></div>
リスト4のHTML要素に対して、リスト5の処理でFlexGridを表示します。
// FlexGridの表示 ...(1) function showFlexGrid(grid) { // 生成済みFlexGridが指定された場合は破棄する ...(2) if (grid != null) grid.dispose(); // 新しいFlexGridを生成 ...(3) const newGrid = new wijmo.grid.FlexGrid('#grid-elem', { itemsSource: salesDataCV, autoGenerateColumns: false, isReadOnly: true, columns: [ { binding: 'date', header: '年月日', width: 110 }, { binding: 'store1', header: '本店', width: 90 }, { binding: 'store2', header: '駅前店', width: 90 }, { binding: 'store3', header: '空港店', width: 90 }, { binding: 'sum', header: '合計', width: 90 } ] }); // 生成したFlexGridを返却 ...(4) return newGrid; } // FlexGridの表示を実行 ...(5) let grid = showFlexGrid(null);
(1)のshowFlexGrid関数でFlexGridを表示します。あとでFlexGridを再描画する際に一度破棄する必要があるため、関数の引数でFlexGridの変数を受け取り、nullでない場合には(2)のdisposeメソッドで破棄するようにします。(3)で新しいFlexGridを生成して、その戻り値を(4)で返却します。wijmo.grid.FlexGridコンストラクターの第1引数に表示先HTML要素のセレクター(ここではリスト4のHTML要素のIDを「#grid-elem」と指定)、第2引数には表2に示すオプションを指定します。
No. | 項目名 | 意味 | 値 |
1 | itemsSource | 表示するデータ | salesDataCV(CollectionView) |
2 | autoGenerateColumns | 列を自動生成するか | false(しない) |
3 | isReadOnly | 読み取り専用にするか | true(する) |
4 | columns | 表示する列の指定 | (表3を参照) |
columnsでは表示する列を指定します。指定内容は表3の通りです。ここではbindingにdate、store1~store3、およびリスト3(2a)で定義した合計列sumを指定して、日次の各店舗の売上と合計売上を表に表示します。
No. | 項目名 | 意味 |
1 | binding | データ属性の名前 |
2 | header | ヘッダー行に表示する文言 |
3 | width | 列の幅 |
最後に(5)でshowFlexGrid関数を実行してFlexGridを表示するとともに、FlexGridに対応した変数gridを戻り値で受け取って保持しておきます。この変数はあとで再描画の時に使います。
FlexChartによるチャート表示
次に、FlexChartで売上データをチャート(棒グラフ)に表示していきます。FlexGrid同様、HTML側にはFlexChartを表示するHTML要素をリスト6の通り記述します。
<div class="chart" id="chart-elem"></div>
FlexChartを表示する処理の実装はリスト7の通りです。
// FlexChartの表示 ...(1) function showFlexChart(chart) { // 生成済みFlexChartが指定された場合は破棄する ...(2) if (chart != null) chart.dispose(); // 新しいFlexChartを生成 ...(3) const newChart = new wijmo.chart.FlexChart('#chart-elem', { itemsSource: salesDataCV, bindingX: 'date', series: [ { binding: 'store1', name: '本店' }, { binding: 'store2', name: '駅前店' }, { binding: 'store3', name: '空港店' } ] }); // 生成したFlexChartを返却 ...(4) return newChart; } // FlexChartの表示を実行 ...(5) let chart = showFlexChart(null);
(1)のshowFlexChart関数でFlexChartを表示します。関数の引数でFlexChartの変数を受け取り、nullでない場合に(2)で破棄する処理は、FlexGridの処理(リスト5)と同様です。(3)で新しいFlexChartを生成して、その戻り値を(4)で返却します。wijmo.grid.FlexChartコンストラクターの第1引数に表示先HTML要素(リスト6)を表すセレクター「#chart-elem」、第2引数には表4に示すオプションを指定します。
No. | 項目名 | 意味 | 値 |
1 | itemsSource | 表示するデータ | salesDataCV(CollectionView) |
2 | bindingX | X軸に対応するデータ属性名 | date |
3 | series | チャートに表示するデータ系列の配列 | (後述) |
seriesにはチャートのデータ系列を配列で指定します。配列要素のbindingにデータ属性名(ここではstore1~store3)、nameにはデータ属性の名前(ここでは店舗名)をそれぞれ指定します。
最後に(5)でshowFlexChart関数を実行してFlexChartを表示します。戻り値で受け取ったFlexChartの変数chartは、FlexGridと同様、再描画時に利用します。
RadialGaugeによるゲージ表示
各店舗のこれまでの売上を合計して、目標額に対する売上達成率をRadialGauge(円形のゲージ)に表示するようにします。1つのゲージに対応するHTML要素はリスト8の通りです。(1)には売上目標と売上合計の文言を表示し、(2)にRadialGaugeを表示します。このようなHTML要素を4つ(3店舗+合計の分)記述します。
<div class="gauge-title">本店</div> <div class="gauge-sales-status" id="gauge-sales-status1"></div> <!--(1)--> <div class="gauge" id="gauge-elem1"></div> <!--(2)-->
RadialGaugeを初期化する処理はリスト9の通りです。wijmo.gauge.RadialGaugeコンストラクターの第1要素にHTML要素のセレクターを設定して実行します。第2要素にオプションを指定することでゲージの表示などをカスタマイズすることができます。今回は各ゲージにgetTextオプションでコールバック関数getTextCallback(1)を指定し、ゲージに表示する文字列に%の記号を連結しています。さらに合計のゲージ(gaugeAll)ではpointerオプションを指定して、ゲージの色を赤にしています。
// RadialGaugeの初期化 const gauge1 = new wijmo.gauge.RadialGauge('#gauge-elem1',{ getText: getTextCallback, }); const gauge2 = new wijmo.gauge.RadialGauge('#gauge-elem2',{ getText: getTextCallback, }); const gauge3 = new wijmo.gauge.RadialGauge('#gauge-elem3',{ getText: getTextCallback, }); const gaugeAll = new wijmo.gauge.RadialGauge('#gauge-elem-all', { getText: getTextCallback, pointer: { color: '#ff0000' } }); // RadialGaugeに表示する文字列に「%」を連結するコールバック関数 ...(1) function getTextCallback(gauge, part, value, text) { return `${text}%`; }
売上達成率を表示する処理の実装はリスト10の通りです。
// 売上達成率の計算と表示 ...(1) function showSalesPercents() { // 売上合計 ...(2) const items = salesDataCV.items; const salesSum1 = items.reduce((s, elem) => s + elem.store1, 0); const salesSum2 = items.reduce((s, elem) => s + elem.store2, 0); const salesSum3 = items.reduce((s, elem) => s + elem.store3, 0); const salesSumAll = items.reduce((s, elem) => s + elem.sum, 0); // 売上目標 ...(3) const salesTarget1 = salesTarget.store1; const salesTarget2 = salesTarget.store2; const salesTarget3 = salesTarget.store3; const salesTargetAll = salesTarget1 + salesTarget2 + salesTarget3; // 売上達成率をゲージに設定 ...(4) gauge1.value = salesSum1 / salesTarget1 * 100; gauge2.value = salesSum2 / salesTarget2 * 100; gauge3.value = salesSum3 / salesTarget3 * 100; gaugeAll.value = salesSumAll / salesTargetAll * 100; // 売上目標と売上合計を表示 ...(5) document.getElementById('gauge-sales-status1').textContent = `¥${Intl.NumberFormat('ja-JP').format(salesSum1)} / ¥${Intl.NumberFormat('ja-JP').format(salesTarget1)}`; (略) } // 売上達成率の計算と表示を実行 ...(6) showSalesPercents();
(1)のshowSalesPercents関数で売上達成率(RadialGaugeと文言)を表示します。(2)は売上合計の計算です。CollectionViewのitemsプロパティからデータ配列を取得して、reduceメソッドでデータ配列のstore1~store3、およびsumを合計します。(3)では各店舗の売上目標を取得して、それらを合計して全体の売上目標とします。ここまで計算した売上合計と売上目標から割合を計算して、(4)でゲージに設定します。(5)は売上目標と売上合計を文字で表示する処理です。最後に(6)でshowSalesPercents関数を実行して表示を行います。
売上データ追加と再描画の処理
このサンプルでは「売上データを追加」ボタンをクリックするとランダムで売上データを追加し、その内容が表示に反映されるようにします。処理内容はリスト11の通りです。
// ランダムな売上を取得する処理 ...(1) function getRandomSales() { return Math.floor(Math.random() * 150000) + 50000; } // 売上データを追加する処理 ...(2) function addSalesData() { const items = salesDataCV.items; // 31日までデータ追加済みの場合は処理しない ...(3) if (items.length >= 31) return; // 新しい日のデータを追加 ...(4) const newDay = items.length + 1; items.push({ 'date': `2024/03/${newDay < 10 ? '0' + newDay : newDay}`, 'store1': getRandomSales(), 'store2': getRandomSales(), 'store3': getRandomSales() }); // FlexGrid、FlexChartを更新 ...(5) grid = showFlexGrid(grid); chart = showFlexChart(chart); // 売上達成率を更新 ...(6) showSalesPercents(); }
(1)はランダムな売上を取得する処理です。ボタンクリック時に売上データを追加する処理は(2)で、31日を超えるデータを追加しないように(3)で判定後、(4)で新しいデータを追加します。新しいデータのdate属性は既存データの続きの年月日、store1~store3属性には(1)の処理で取得したランダムな売上を設定します。
売上データ追加後、(5)でFlexGridとFlexChartを更新します。現在表示されているFlexGridとFlexChartに対応する変数grid、chartを渡すことで、それまでの表示をいったん破棄して新たに生成します。それまでの表示を破棄するのは、追加されたデータを表示に反映するためです。最後に(6)で、売上達成率を表示するRadialGaugeとテキストを更新します。
まとめ
本記事では、WijmoのUI部品であるFlexGrid、FlexChart、RadialGauge、およびデータを操作するCollectionViewを利用して、店舗の売上達成状況を想定したダッシュボードWebページのサンプルを作成しました。各UI部品の得意分野を組み合わせることにより、表現力豊かにデータを可視化できます。