SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

高機能JavaScriptグリッド部品「SpreadJS」の活用(AD)

Excelライクな「SpreadJS」で、Web APIから取得したデータを集計するページをAngular+ASP.NET Core環境で作ろう!

  • このエントリーをはてなブックマークに追加

 本記事では、ExcelライクなスプレッドシートをWebページに表示できるグレープシティのJavaScriptライブラリ「SpreadJS」を、JavaScriptフレームワークAngularと組み合わせて利用する例を紹介します。サーバーサイドはマイクロソフトのASP.NET CoreでWeb APIを作成して、Web APIから取得したデータをSpreadJSで表示させます。

  • このエントリーをはてなブックマークに追加

はじめに

 SpreadJSは、ExcelライクなスプレッドシートをWebページに表示できる、グレープシティのJavaScriptライブラリです。現状の最新版は2022年7月に公開された「V15.1J」です。

図1 SpreadJSの製品ページ
図1 SpreadJSの製品ページ

 SpreadJSの活用法は過去記事でも紹介してきましたが、今回はクライアント(Webページ)にSpreadJSとAngularの組み合わせ、サーバー側はASP.NET Coreを利用して、スプレッドシートを表示する方法を紹介します。計算式を設定して最大値などの集計を行う方法も説明します。

対象読者

  • ExcelライクなスプレッドシートをWebページで実現したい方
  • ASP.NET CoreのWeb API実装を体験したい方
  • Web APIのデータをクライアント側で集計する事例を知りたい方

必要な環境

 本記事のサンプルコードは、以下の環境で動作を確認しています。

  • Windows 10 64bit版
  • SpreadJS 15.1.4
  • Microsoft Visual Studio Community 2022 17.3.1
  • Node.js 16.17.0 64bit版
  • Microsoft Edge 104.0.1293.54

 サンプルコードに含まれるソリューションファイル(*.sln)をVisual Studio 2022で開くことで、ソースコードの確認や実行が行えます。

Visual Studio 2022でAngularプロジェクトを作成

 本記事では、Visual Studio 2022の「Angular の ASP.NET Core」プロジェクトテンプレートをもとにSpreadJSを設定します。このテンプレートでプロジェクトを作って実行すると、複数のページをリンクで切り替え表示できる図2のWebページが表示されます。

図2 Visual Studioで生成できるAngularアプリの実行結果(P001AngularPlain)
図2 Visual Studioで生成できるAngularアプリの実行結果(P001AngularPlain)

 このプロジェクトの構造を簡単に説明します。ソリューションファイルと同じ階層のP001AngularPlainフォルダー(図3)内で、ClientAppフォルダーがAngularを利用したクライアント側のコード、それ以外がASP.NET Coreを利用したサーバー側のコードです。

図3 AngularプロジェクトのP001AngularPlainフォルダー(P001AngularPlain)
図3 AngularプロジェクトのP001AngularPlainフォルダー(P001AngularPlain)

 ClientAppフォルダー内には、プロジェクトの定義ファイルpackage.jsonなどが存在します。Webページ実装の実体は、srcサブフォルダー内(図4)に存在します。

図4 AngularプロジェクトのClientApp/srcフォルダー(P001AngularPlain)
図4 AngularプロジェクトのClientApp/srcフォルダー(P001AngularPlain)

 最初に実行されるのがmain.jsで、それが内部的にWebページ全体のコンポーネント(AppComponent)を起動して画面を表示します。AppComponentのファイルは、src/appフォルダー内に存在します(図5)。

図5 Angularコンポーネントが存在するsrc/appフォルダー(P001AngularPlain)
図5 Angularコンポーネントが存在するsrc/appフォルダー(P001AngularPlain)

 componentsフォルダーには、AppComponentを構成するapp.component.***ファイルのほか、フォルダーに分けられて表1のコンポーネントが存在します。すべてのコンポーネントはapp.module.tsにより1つのモジュールに含まれるようになっています。

表1 appフォルダー内に存在するコンポーネント(P001AngularPlain)
No. フォルダー名 役割
1 counter Counterページに対応するコンポーネント
2 fetch-data Fetch dataページに対応するコンポーネント
3 home Homeページに対応するコンポーネント
4 nav-menu 画面上部メニューのコンポーネント

 app.module.tsファイルには各コンポーネントに対応するパス(ルート)が定義されています(リスト1)。例えばFetchDataComponentには(3)でルート「fetch-data」が設定されるため、「http://<ホスト名とポート>/fetch-data」にアクセスして表示できます。

[リスト1]コンポーネントを切り替えて表示するルーターの設定(P001AngularPlain/P001AngularPlain/ClientApp/src/app/app.modules.ts)
RouterModule.forRoot([
  { path: '', component: HomeComponent, pathMatch: 'full' }, // Home ...(1)
  { path: 'counter', component: CounterComponent }, // Counter ...(2)
  { path: 'fetch-data', component: FetchDataComponent }, // Fetch data ...(3)
])

 以上を踏まえて、本記事ではhome、counter、fetch-dataと並列にコンポーネントを1つ追加して、そこにSpreadJSでスプレッドシートを表示するように実装していきます。

過去1カ月の気温を表示するWebページを作る

 本記事では、気象庁のWebページから取得した過去1カ月の最高気温・最低気温をサーバーのWeb APIで提供し、それをSpreadJSで表示するように実装していきます(図6)。気温データそのものに加え、クライアント側で気温データの最大値・最小値・平均値を集計して表示します。

図6 SpreadJSで気温のデータを表示するサンプル(P002AngularSpreadJS)
図6 SpreadJSで気温のデータを表示するサンプル(P002AngularSpreadJS)

 過去1カ月の最高気温・最低気温は、気象庁の「過去の気象データ・ダウンロード」から、2022年7月の札幌のデータをCSVファイルでダウンロードします。今回は実装を単純にするため、CSVファイルに対して手動で内容の整形とヘッダー付加を行い、図7の状態でプロジェクトに追加します。

図7 Web APIで提供する気温のデータ(P002AngularSpreadJS/P002AngularSpreadJS/data.csv)
図7 Web APIで提供する気温のデータ(P002AngularSpreadJS/P002AngularSpreadJS/data.csv)

 以下では、このデータをWebページ上に表示する実装を、サーバー側(ASP.NET CoreのWeb API)とクライアント側(SpreadJS)とに分けて順に説明していきます。

サーバー側の実装

 サーバー側でCSVファイルを読み出せるように、NuGet パッケージ マネージャーで「CsvHelper」パッケージを検索してインストールします。

図8 CsvHelperパッケージをインストール(P002AngularSpreadJS)
図8 CsvHelperパッケージをインストール(P002AngularSpreadJS)

 Controllers配下にC#のファイルを生成して、Web API処理をリスト2の通り実装します。

[リスト2]Web APIの実装(P002AngularSpreadJS/P002AngularSpreadJS/Controllers/TemperatureController.cs)
// 気温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)でCSV内容をレコードのリストにして返却します。ASP.NET Coreの処理により、リスト内容がJSON文字列に変換されてAPIの戻り値になります。なお(5)で型設定されるTemperatureDataは、日時、最高気温、最低気温のプロパティを含むクラスです。詳細はサンプルコードを参照してください。

クライアント側の実装

 ClientAppフォルダーでリスト3を実行して、プロジェクトにSpreadJSを追加します。

[リスト3]SpreadJSパッケージをプロジェクトに追加するコマンド
npm install @grapecity/spread-sheets                # ...SpreadJS本体
npm install @grapecity/spread-sheets-angular        # ...Angularモジュール
npm install @grapecity/spread-sheets-resources-ja   # ...日本語リソース

 SpreadJSのCSSが参照されるよう、angular.jsonファイルにリスト4の通り記述します。

[リスト4]SpreadJSのCSSを参照する記述(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/angular.json)
"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.min.css",
  // ↓この行を追加
  "node_modules/@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016darkGray.css",
  "src/styles.css"
],

 app.module.tsのimports内に、リスト5の通りSpreadJSのモジュール(SpreadSheetsModule)を追加してインポートします。

[リスト5]AppモジュールからSpreadJSのモジュールを参照する記述(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/app.modules.ts)
@NgModule({
  imports: [
    SpreadSheetsModule,
(略)
})

 次に、スプレッドシートを表示するSpreadSampleコンポーネントを、Angular CLIのngコマンド(リスト6)で追加します。ngコマンドの次の「g」は生成(generate)の意味で、--moduleオプションはコンポーネントを追加するモジュール(ここではappモジュール)を指定します。コマンドによりSpreadSampleコンポーネントのファイルがsrc/app/spread-sampleフォルダーに生成されます。

[リスト6]スプレッドシートを表示するコンポーネントを追加するコマンド
ng g component SpreadSample --module app

 生成されたspread-sampleフォルダー配下のファイルを編集していきます。コンポーネントの表示内容に対応するテンプレート(htmlファイル)には、リスト7の通り記述します。

[リスト7]SpreadSampleコンポーネントのテンプレート記述(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/spread-sample/spread-sample.component.html)
<gc-spread-sheets [hostStyle]="hostStyle"
  (workbookInitialized)="workbookInit($event)"> <!--(1)-->
  <gc-worksheet name="気温" [dataSource]="temperatures"
    [autoGenerateColumns]="false"> <!--(2)-->
    <gc-column headerText="日時" dataField="date" width="200">
    </gc-column> <!--(3)-->
    <gc-column headerText="最高気温" dataField="maxTemperature" width="100"
      formatter="#.0"></gc-column>
    <gc-column headerText="最低気温" dataField="minTemperature" width="100"
      formatter="#.0"></gc-column>
  </gc-worksheet>
</gc-spread-sheets>

 まずスプレッドシート全体を表す<gc-spread-sheets>コンポーネント(1)を記述します。[hostStyle]には後述する表示サイズのオブジェクトhostStyleを指定します。(workbookInitialized)はスプレッドシート初期化時のイベントハンドラーメソッドで、後ほど実装するworkbookInitメソッドを呼ぶようにします。

 <gc-spread-sheets>内に、1つのワークシートに対応した<gc-worksheet>コンポーネント(2)を記述します。name属性はワークシートの名前、dataSourceは後述するデータ変数temperaturesを指定します。[autoGenerateColumns]にはfalseを設定して、データに合わせて自動で列が生成されないようにします。

 <gc-worksheet>内には(3)の通り、データを表示する列に対応した<gc-column>コンポーネントを、列の数だけ記述します。dataFieldはデータの属性名、headerTextはヘッダー部の文言、widthは列の幅指定、formatterはデータの表示フォーマットです。formatterの"#.0"は、数字を小数第一位まで表示することを表します。

 リスト7に対応するコンポーネントの実装は、リスト8の通りです。

[リスト8]SpreadSampleコンポーネントの実装(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/spread-sample/spread-sample.component.ts)
// SpreadJSをインポート ...(1)
import '@grapecity/spread-sheets-resources-ja';
import * as GC from '@grapecity/spread-sheets';
// カルチャ設定 ...(2)
GC.Spread.Common.CultureManager.culture('ja-jp');
// ライセンスキー設定 ...(3)
GC.Spread.Sheets.LicenseKey = '<ライセンスキー>';
@Component({
  selector: 'app-spread-sample',
  templateUrl: './spread-sample.component.html',
  styleUrls: ['./spread-sample.component.css']
})
export class SpreadSampleComponent {
  // スプレッドシートの表示サイズ ...(4)
  hostStyle = { width: '100%', height: '500px' };
  // 受信した気温データを格納する変数 ...(5)
  temperatures: Temperature[] = [];
  // SpreadJSのスプレッドシートを保持する変数 ...(6)
  spread: GC.Spread.Sheets.Workbook | undefined;
  // コンストラクター(依存性注入でHttpClient変数httpを受け取る) ...(7)
  constructor(private http: HttpClient) {
  }
  // ワークブック初期化後の処理 ...(8)
  workbookInit(args: any) {
    // スプレッドシートのオブジェクトを取得して保持 ...(9)
    this.spread = args.spread;
    // Web APIからデータ取得 ...(10)
    this.http.get<Temperature[]>('/api/Temperature').subscribe({
      // 取得時の処理
      next: result => {
        // データを変数に保持 ...(11)
        this.temperatures = result;
        // ワークシートを装飾 ...(12)
        this.decorateWorksheet();
      },
      // エラー時の処理
      error: (e) => console.error(e)
    });
  }
(略)
}

 (1)で、SpreadJSと日本語リソースのパッケージをインポートします。(2)はスプレッドシートで日本語を使用するカルチャの設定、(3)はライセンスキーの設定です。ライセンスキーを設定しないとトライアル版として動作し、スプレッドシート内にトライアル版である旨の透かしが表示されます。

 コンポーネント内には、(4)~(6)のプロパティを設定します。各プロパティの内容は表2の通りです。

表2 リスト8で実装されているプロパティ
No. 名前 意味
(4) hostStyle スプレッドシートのスタイル(ここでは幅と高さ)
(5) temperatures スプレッドシートに表示する気温データ
(6) spread Webページで表示するSpreadJSのスプレッドシート

 コンストラクター(7)では、HTTP通信を行うAngularのHttpClientオブジェクトhttpを依存性注入で受け取ります。ワークブック初期化後に実行されるworkbookInit(8)では、まず(9)でイベント引数からスプレッドシートのオブジェクトを取得して保持した後、(10)のhttp.getメソッドでWeb API(/api/Temperature)からデータを取得して、(11)で取得データをtemperatures変数に格納します。(12)については後述します。

 追加したSpreadSampleコンポーネントを表示できるようにするため、パス(ルート)を設定するapp.module.tsのRouteModuleに、リスト9の内容を追加します。

[リスト9]SpreadSampleコンポーネントのルート設定(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/app.module.ts)
RouterModule.forRoot([
(略)
  { path: 'spread-sample', component: SpreadSampleComponent }
])

 リスト9で追加したパスを、nav-menuコンポーネントにリスト10の通り設定します。

[リスト10]SpreadSampleコンポーネントへのリンク(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/nav-menu/nav-menu.component.html)
<li class="nav-item" [routerLinkActive]="['link-active']">
  <a class="nav-link text-dark" [routerLink]="['/spread-sample']"
    >SpreadJS</a>
</li>

スプレッドシートのヘッダー行に集計行を追加

 次に、スプレッドシートのヘッダー行に集計行を追加します。リスト8(13)のdecorateWorksheetメソッドを、リスト11の通り実装します。

[リスト11]集計行を表示する実装(P002AngularSpreadJS/P002AngularSpreadJS/ClientApp/src/app/spread-sample/spread-sample.component.ts)
decorateWorksheet() {
  // ワークシートを取得 ...(1)
  const sheet = this.spread!.getActiveSheet();
  // ヘッダーが4行になるよう設定 ...(2)
  sheet.setRowCount(4, GC.Spread.Sheets.SheetArea.colHeader);
  // 最大値ヘッダー行のスタイルを定義 ...(3)
  const style1 = new GC.Spread.Sheets.Style()
  style1.backColor = '#ffe6d5';
  style1.foreColor = '#ff0000';
  style1.formatter = "#.0";
  // 最大値ヘッダー行に文字を指定 ...(4)
  sheet.setValue(0, 0, '最大値', GC.Spread.Sheets.SheetArea.colHeader);
  // 最大値ヘッダー行にスタイルを指定 ...(5)
  sheet.setStyle(0, -1, style1, GC.Spread.Sheets.SheetArea.colHeader);
  // 最大値ヘッダー行に数式を指定 ...(6)
  // 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);
(以下略:最小値と平均値の行をヘッダーに設定)
}

 (1)で処理対象のワークシートを取得します。「this.spread!」は、spread変数がnullである可能性を無視してプロパティを取得するTypeScriptの記法です。(2)ではsheet.setRowCountメソッドで、ヘッダー行が4行になるように指定しています。

 (3)でGC.Spread.Sheets.StyleクラスのオブジェクトにbackColor(背景色)、foreColor(文字色)、formatter(表示フォーマット)を設定します。

 (4)のsetValueメソッドでセルの値を、(5)のsetStyleメソッドでセルのスタイルを、(6)のsetFormulaメソッドでセルの数式を、それぞれヘッダー行に設定します。各メソッドの第1引数は行番号、第2引数は列番号、第3引数は設定値です。第4引数にはGC.Spread.Sheets.SheetArea.colHeaderを設定して、ヘッダー行に対する処理であることを表します。(5)の第2引数(列番号)「-1」は、すべての列への指定を意味します。

まとめ

 本記事では、グレープシティのJavaScriptスプレッドシート部品SpreadJSを、クライアント側にAngular、サーバー側にASP.NET Coreの環境で利用する方法を説明しました。SpreadJSのExcelライクなスプレッドシート機能により、Web APIから取得したデータをクライアント側で集計して表示できます。

参考資料

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/16472 2022/09/28 12:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング