過去1カ月の気温をスプレッドシートに表示するWebページを作る
本記事でSpreadJSに表示する題材は、過去1カ月の最高気温・最低気温です。これらをサーバー側のWeb APIで提供し、それをSpreadJSで表示するように実装していきます(図4)。このサンプルでは最高気温・最低気温そのもの以外に、SpreadJSのスプレッドシート機能を利用して、クライアント側で最大値・最小値・平均値を集計して表示します。
表示するデータは、気象庁の「過去の気象データ・ダウンロード」から、2022年9月の大阪のデータをCSVファイルでダウンロードして利用します。ダウンロードしたCSVファイルは事前に内容の整形とヘッダー付加を行い、図5の状態にしておきます。
以下では、このデータをWebページ上に表示する実装を、サーバー側(ASP.NET Coreで実装するWeb API)とクライアント側(Vue.js、SpreadJS)とに分けて説明していきます。
サーバー側の実装
サーバー側には、図5のCSVファイルを読みだして、戻り値として返却するWeb APIを実装します。このサーバー側実装内容は、過去記事1、過去記事2のサンプルと同じものです。
サーバー側でCSVファイルを読み出すため、NuGet パッケージ マネージャーでCSVファイル用ライブラリの「CsvHelper」パッケージをインストール後、Web APIのクラスが配置されるControllers配下に、Web API処理をリスト1の通り実装します。
// 気温Web APIのコントローラー ...(1) [Route("api/[controller]")] [ApiController] public class TemperatureController : ControllerBase { // GET時の処理 ...(2) [HttpGet] public object Get() { // CSVファイルを読み込むReaderを生成 ...(3) using (var reader = new StreamReader("data.csv")) { // CSV内容を読み込むReaderを生成 ...(4) using (var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture))) { // CSVファイルからレコードを取得して返却 ...(5) var records = csv.GetRecords<TemperatureData>(); return records.ToList(); } } } }
クラスに付与した(1)の[Route("api/[controller]")]、[ApiController]属性により、「api/Temperature」というパスに対応するWeb APIであることを指定します。Getメソッド(2)に付与した[HttpGet]属性は、HTTP GET時の処理であることを表します。
Getメソッド内では(3)および(4)でCSV内容を読み込むCsvReaderを生成し、(5)で内容をレコードのリストにして返却します。ASP.NET Coreの処理により、リスト内容がJSON文字列に変換されてAPIの戻り値として返却されます。
クライアント側の実装
クライアント側の実装は、Vue.jsプロジェクトにSpreadJSを追加する方法が公式ドキュメントで案内されているので、基本的にその手順に従って進めます。最初にClientAppフォルダーでリスト2のコマンドを実行して、プロジェクトにSpreadJSを追加します。コマンド末尾の「@15.2.4」は、インストールするパッケージのバージョン指定です。
npm install @grapecity/spread-sheets-vue@15.2.4 # ...Vue.js用モジュール npm install @grapecity/spread-sheets-resources-ja@15.2.4 #...日本語リソース
Webページ表示時、最初に実行されるmain.jsに、リスト3の通り実装します。SpreadJSのスプレッドシート、ワークシート、列にそれぞれ対応するGcSpreadSheets、GcWorkSheet、GcColumnを(1)でインポートします。Vue.jsの初期化処理(2)で得られるVue.jsアプリのインスタンスappに対して、(3)のcomponentメソッドで、SpreadJSのコンポーネントにそれぞれ「gc-spread-sheets」「gc-worksheet」「gc-column」という名前を指定します。その後(4)でVue.jsアプリをマウント(表示)します。
// SpreadJSコンポーネントをインポート ...(1) import { GcSpreadSheets, GcWorksheet, GcColumn } from '@grapecity/spread-sheets-vue' // Vue.jsの初期化処理 ...(2) const app = createApp(App); // SpreadJSのコンポーネント指定 ...(3) app.component('gc-spread-sheets', GcSpreadSheets); app.component('gc-worksheet', GcWorksheet); app.component('gc-column', GcColumn); // Vue.jsアプリをマウント ...(4) app.mount('#app');
ページ全体に対応するAppコンポーネント(App.vue)に、SpreadJSのコンポーネントを設定していきます。画面の表示内容を表す<template>部はリスト4の通りです。
<template> <div id="spread-host"> <gc-spread-sheets :hostStyle="hostStyle" @workbookInitialized="workbookInit"> <!--(1)--> <gc-worksheet name="気温" :dataSource="temperatures"> <!--(2)--> <gc-column headerText="日時" dataField="date" width="200"> <!--(3)--> </gc-column> <gc-column headerText="最高気温" dataField="maxTemperature" width="200"> </gc-column> <gc-column headerText="最低気温" dataField="minTemperature" width="200"> </gc-column> </gc-worksheet> </gc-spread-sheets> </div> </template>
まず、スプレッドシート全体を表す<gc-spread-sheets>コンポーネント(1)を記述します。スプレッドシートの表示スタイルを表すhostStyleには後述するサイズ指定(hostStyle変数)を反映します。また、workbookInitializedはスプレッドシート初期化時のイベントハンドラーメソッドで、後ほど実装するworkbookInitメソッドを指定します。
<gc-spread-sheets>内に、1つのワークシートに対応した<gc-worksheet>コンポーネント(2)を記述します。nameはワークシートの名前、dataSourceは後述するデータ変数temperaturesを指定します。
<gc-worksheet>内には(3)の通り、データを表示する列に対応した<gc-column>コンポーネントを、列の数だけ記述します。dataFieldはデータの属性名、headerTextはヘッダー部の文言、widthは列の幅指定です。
一方、画面の処理内容は<script>部に、リスト5の通り実装します。
<script setup> import { nextTick, ref } from 'vue' // SpreadJSをインポート ...(1) import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016darkGray.css' import * as GC from '@grapecity/spread-sheets' import '@grapecity/spread-sheets-resources-ja' // カルチャ設定 ...(2) GC.Spread.Common.CultureManager.culture('ja-jp') // ライセンスキー設定 ...(3) GC.Spread.Sheets.LicenseKey = '<ライセンスキー>' // スプレッドシートの表示サイズ ...(4) const hostStyle = { width: '100%', height: '500px' } // 受信した気温データを格納する変数 ...(5) let temperatures = ref([]) // ワークブック初期化後の処理 ...(6) const workbookInit = (args) => { // Web APIからデータ取得 ...(7) fetch('/api/Temperature') .then(res => res.json()) .then(result => { // データを変数に保持 ...(8) temperatures.value = result // ワークシートを装飾 ...(9) nextTick(() => { decorateWorksheet(args) // Webページにデータが反映されてから処理 }) }) } (略) </script>
(1)でSpreadJSのCSSとJavaScriptをインポートします。(2)はカルチャの設定で、日本語(ja-jp)を設定します。(3)はライセンスキーの設定です。ライセンスキーを設定しない場合は、トライアル版である旨の透かしがスプレッドシートに表示されます。
コンポーネント内で、スプレッドシートの表示サイズを表すhostStyle(4)を記述します。また、スプレッドシートに表示する温度データを格納する配列temperatures(5)は、変数をリアクティブにして、データの変更を画面に反映させるため、Vue.jsのComposition APIが提供するrefメソッドを利用して記述します。初期値は空の配列です。
ワークブック初期化後に実行されるworkbookInitメソッド(6)では、(7)のfetchメソッドでWeb API(/api/Temperature)からデータを取得して、(8)でtemperatures.valueに設定します(この処理により、Web APIから取得したデータが画面に反映されます)。
(9)は後述するスプレッドシートの装飾処理です。メソッド引数にarg(=workbookInitで渡されてくるSpreadJSのインスタンス)を渡して実行します。ここではVue.jsが提供するnextTickメソッドにより、Webページにデータが反映されてから装飾処理が実行されるようにします。
スプレッドシートのヘッダー行に集計行を追加
リスト5(9)のdecorateWorksheetメソッドには、スプレッドシートを装飾する処理を、リスト6の通り実装します。
const decorateWorksheet = (spread) => { // ワークシートを取得 ...(1) const sheet = spread.getActiveSheet() // 最高気温・最低気温列のフォーマット設定 ...(2) sheet.setFormatter(-1, 1, "#.0", GC.Spread.Sheets.SheetArea.viewport) sheet.setFormatter(-1, 2, "#.0", GC.Spread.Sheets.SheetArea.viewport) // ヘッダーが4行になるよう設定 ...(3) sheet.setRowCount(4, GC.Spread.Sheets.SheetArea.colHeader) // 最大値ヘッダー行のスタイルを定義 ...(4) const style1 = new GC.Spread.Sheets.Style() style1.backColor = '#ffe6d5' style1.foreColor = '#ff0000' style1.formatter = "#.0" // 最大値ヘッダー行に文字を指定 ...(5) sheet.setValue(0, 0, '最大値', GC.Spread.Sheets.SheetArea.colHeader) // 最大値ヘッダー行にスタイルを指定 ...(6) sheet.setStyle(0, -1, style1, GC.Spread.Sheets.SheetArea.colHeader) // 最大値ヘッダー行に数式を指定 ...(7) // 2列目:「=MAX(<シート名>!B:B)」、3列目:「=MAX(<シート名>!C:C)」 sheet.setFormula(0, 1, `=MAX(${sheet.name()}!B:B`, GC.Spread.Sheets.SheetArea.colHeader) sheet.setFormula(0, 2, `=MAX(${sheet.name()}!C:C`, GC.Spread.Sheets.SheetArea.colHeader) (以下略:最小値と平均値の行をヘッダーに設定) }
引数に渡されるSpreadJSのオブジェクトspreadのgetActiveSheetメソッド(1)で、処理対象のワークシートを取得します。(2)は、スプレッドシートの最高気温、最低気温の列にフォーマットを設定する処理です。setFormatterメソッドの第1引数は行(-1はすべての行を表す)、第2引数は列、第3引数の「#.0」は数字を小数第一位まで表示することを表し、第4引数のGC.Spread.Sheets.SheetArea.viewportはビューポート(つまりワークシートの本体)に対する処理であることを表します。
(3)ではsheet.setRowCountメソッドで、ヘッダー行が4行になるように指定しています。第2引数のGC.Spread.Sheets.SheetArea.colHeaderは、ヘッダー行に対する処理であることを表します。
(4)でGC.Spread.Sheets.StyleクラスのオブジェクトにbackColor(背景色)、foreColor(文字色)、formatter(表示フォーマット)を設定します。
(5)のsetValueメソッドでセルの値を、(6)のsetStyleメソッドでセルのスタイルを、(7)のsetFormulaメソッドでセルの数式を、それぞれヘッダー行に設定します。各メソッドの第1引数は行番号、第2引数は列番号、第3引数は設定値です。第4引数のGC.Spread.Sheets.SheetArea.colHeaderはヘッダー行を表し、(5)の第2引数(列番号)「-1」は、すべての列への指定を意味します。
以上の実装により、図4のスプレッドシートをWebブラウザーに表示できます。Web APIから取得できるのは最高気温と最低気温の生データだけですが、SpreadJSのスプレッドシート機能により、最大値・最小値・平均値をクライアント側の処理により表示できます。