Blazor WebAssemblyでJavaScriptスプレッドシートライブラリを使う

ASP.NET Core Blazorでは、.NETのメソッドからJavaScriptの関数を呼び出したり、逆にJavaScriptの関数から.NETのメソッドを呼び出したりする、「JavaScript相互運用」が可能です。

今回の記事では、Blazor WebAssemblyのアプリケーション上で、ExcelライクなJavaScriptスプレッドシートライブラリ「SpreadJS(スプレッドJS)」のラッパーを作成して使用する方法をご紹介します。

Blazorアプリケーションの作成

開発環境

  • Visual Studio 2019(Version 16.7.4)
  • SpreadJS V13.2J(13.2.3)

まずはBlazor WebAssemblyのアプリケーションを作成します。Blazor WebAssemblyについてはこちらの記事もご覧ください。

Visual Studioのプロジェクトテンプレートから[Blazor アプリ]を選択します。

Blazorアプリの選択

プロジェクト名を「SpreadJS-Blazor-App」として進みます。

プロジェクト名の設定

次にホスティングモデルを選択します。今回は[Blazor WebAssembly App]を選択します。

ホスティングモデルの選択

Blazorコンポーネントの作成

Blazor WebAssemblyのプロジェクトを作成したら、次はそこにBlazor用のコンポーネントを追加していきます。

ソリューションを右クリックし、[追加]⇒[新しいプロジェクト]を選択し、[Razor クラス ライブラリ]を追加します。

Razorクラスライブラリの追加

プロジェクト名を「SpreadJS_Blazor_Lib」として進みます。

Razorクラスライブラリのプロジェクト名を設定

プロジェクトが作成されたら、SpreadJSの製品版、またはトライアル版のZipファイルからJSファイルとCSSファイルをwwwroot配下にコピーします。

SpreadJSのファイルをコピー

exampleJsInterop.jsを以下のように修正します。こちらのコードがC#からJavaScriptのコードを実行するためのブリッジの役割を果たします。

window.sjsAdaptor = {
init: function (host, config) {
// GC.Spread.Sheets.LicenseKey = "ライセンスキーがある場合は設定します。";
if (config.hostStyle) {
var hostStyle = config.hostStyle;
var styles = hostStyle.split(';');
styles.forEach((styleStr) => {
var style = styleStr.split(':');
host.style[style[0]] = style[1];
});
delete config.hostStyle;
}
return new GC.Spread.Sheets.Workbook(host, config);
},
setCulture: function (locale) {
GC.Spread.Common.CultureManager.culture(locale);
}
};

プロジェクトにデフォルトで追加されるComponent1.razorのファイル名をSpreadJS.razorに変更し、以下のコードを記述します。

@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<div @ref="host"></div>
@code {
[Parameter]
public int SheetCount { get; set; }
[Parameter]
public string HostStyle { get; set; }
private ElementReference host;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
JSRuntime.InvokeVoidAsync("sjsAdaptor.setCulture", "ja-jp");
JSRuntime.InvokeVoidAsync("sjsAdaptor.init", host, new Dictionary<string, object>() {
{ "sheetCount", SheetCount},
{ "hostStyle", HostStyle }
});
}
}
}

BlazorアプリケーションからBlazorコンポーネントを呼び出す

次に、作成したBlazorコンポーネントをBlazorアプリケーションから参照して実行します。

「SpreadJS_Blazor_App」プロジェクトを右クリックし、[追加]⇒[プロジェクト参照]をクリックし、「SpreadJS_Blazor_Lib」を追加します。

プロジェクト参照を追加

「SpreadJS_Blazor_App」のPages/Index.razorを以下のように修正します。

@page "/"
@using SpreadJS_Blazor_Lib
<h1>Hello, SpreadJS!</h1>
<SpreadJS SheetCount="3" HostStyle="@HostStyle" />
@code {
private string HostStyle { get; set; } = "width:90wh;height:70vh;border: 1px solid darkgray";
}

wwwroot/index.htmlを以下のように修正して、SperadJSのスクリプトとCSSファイルへの参照を追加します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="_content/SpreadJS_Blazor_Lib/SpreadJS/css/gc.spread.sheets.excel2013white.13.2.3.css" rel="stylesheet" />
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/gc.spread.sheets.all.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/resources/ja/gc.spread.sheets.resources.ja.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/exampleJsInterop.js" type="text/javascript"></script>
</head>
<body>
<app>Loading...</app>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

「SpreadJS_Blazor_App」をスタートアッププロジェクトに設定して実行すると、以下のようにBlazorの画面上にSpreadJSが表示できます。

BlazorでSpreadJSを表示

SpreadJSの機能の使用

SpreadJSをBlazorの画面に表示することができたので、次はSpreadJSの各種機能を使用してみます。今回は指定したセルに値を設定する「setValueメソッド」と「Excelファイルのインポート機能」をそれぞれ追加してみます。

「SpreadJS_Blazor_Lib」のexampleJsInterop.jsを以下のように書き換えます。

window.sjsAdaptor = {
init: function (host, config) {
//GC.Spread.Sheets.LicenseKey = "ライセンスキーがある場合は設定します。";
if (config.hostStyle) {
var hostStyle = config.hostStyle;
var styles = hostStyle.split(';');
styles.forEach((styleStr) => {
var style = styleStr.split(':');
host.style[style[0]] = style[1];
});
delete config.hostStyle;
}
return new GC.Spread.Sheets.Workbook(host, config);
},
setCulture: function (locale) {
GC.Spread.Common.CultureManager.culture(locale);
},
setValue: function (host, sheetIndex, row, col, value) {
var spread = GC.Spread.Sheets.findControl(host);
if (spread) {
var sheet = spread.getSheet(sheetIndex);
sheet.setValue(row, col, value);
}
},
openExcel: function (host, inputFile) {
var spread = GC.Spread.Sheets.findControl(host);
if (spread) {
var excelIO = new GC.Spread.Excel.IO();
excelIO.open(inputFile.files[0], function (json) {
spread.fromJSON(json);
})
}
}
};

SpreadJS.razorを以下のように書き換えます。

@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<div @ref="host"></div>
@code {
[Parameter]
public int SheetCount { get; set; }
[Parameter]
public string HostStyle { get; set; }
private ElementReference host;
public void setValue(int sheetIndex, int row, int col, object value)
{
JSRuntime.InvokeVoidAsync("sjsAdaptor.setValue", host, sheetIndex, row, col, value);
}
public void OpenExcel(ElementReference inputFile)
{
JSRuntime.InvokeVoidAsync("sjsAdaptor.openExcel", host, inputFile);
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
JSRuntime.InvokeVoidAsync("sjsAdaptor.setCulture", "ja-jp");
JSRuntime.InvokeVoidAsync("sjsAdaptor.init", host, new Dictionary<string, object>() {
{ "sheetCount", SheetCount},
{ "hostStyle", HostStyle }
});
}
}
}

「SpreadJS_Blazor_App」のwwwroot/index.htmlを以下のように書き換えます。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="_content/SpreadJS_Blazor_Lib/SpreadJS/css/gc.spread.sheets.excel2013white.13.2.3.css" rel="stylesheet" />
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/gc.spread.sheets.all.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/resources/ja/gc.spread.sheets.resources.ja.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/interop/gc.spread.excelio.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/plugins/gc.spread.sheets.shapes.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/SpreadJS/scripts/plugins/gc.spread.sheets.charts.13.2.3.min.js" type="text/javascript"></script>
    <script src="_content/SpreadJS_Blazor_Lib/exampleJsInterop.js" type="text/javascript"></script>
</head>
<body>
<app>Loading...</app>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

Pages/Index.razorを以下のように修正します。

@page "/"
@using SpreadJS_Blazor_Lib
<h1>Hello, SpreadJS!</h1>
<table>
<tr>
<td>
<label>シート</label>
<input @bind-value="@SheetIndex" />
</td>
<td>
<label>行</label>
<input @bind-value="@Row" />
</td>
<td>
<label>列</label>
<input @bind-value="@Column" />
</td>
<td>
<lable>値</lable>
<input @bind-value="@Value" />
</td>
</tr>
<tr>
<td>
<button @onclick="doSomething">アップデート</button>
</td>
</tr>
<tr>
<td>
<input type="file" @ref="inputFileEle" @onchange="ImportExcel" />
</td>
</tr>
</table>
<br />
<SpreadJS SheetCount="3" HostStyle="@HostStyle" @ref="ss" />
@code {
private SpreadJS ss;
private ElementReference inputFileEle;
public int SheetIndex { get; set; } = 0;
public int Row { get; set; } = 0;
public int Column { get; set; } = 0;
public string Value { get; set; } = "";
private string HostStyle { get; set; } = "width:90wh;height:70vh;border: 1px solid darkgray";
private void doSomething()
{
ss.setValue(SheetIndex, Row, Column, Value);
}
private void ImportExcel()
{
ss.OpenExcel(inputFileEle);
}
}

実行結果

実行結果は以下の通りです。

setValueを実行

Excelインポートを実行

おわりに

JavaScript相互運用は、ファイルアクセスなどBlazorに足りない機能を補うために欠かせないものです。また、SpreadJSのような高機能なJavaScriptライブラリをそのまま使えるというのも魅力の一つです。

今回作成したサンプルは以下で公開しています。

製品WebサイトではSpreadJSの機能を手軽に試せるデモをご用意していますので、ぜひご覧ください。