はじめに
第1、2回では、「SpreadJS」の基本的な使い方や、サーバー側のWebフレームワークとしてASP.NET MVCの現状を確認しました。
本稿では、SpreadJSとサーバー側を連携し、実際にとあるシナリオを想定して帳票Webアプリを作成します。サンプルの一部を抜粋しながら、以下の機能がどのように実装できるか、紹介します。
想定シナリオ
- "請求書"をブラウザ上で表示・編集し、データをサーバー側に保存する。
- 作成した"請求書"を再度読みこむ。
- 作成した"請求書"を、印刷・Excelファイルとして出力する。
構成
- サーバー側 Web フレームワーク:ASP.NET MVC、ASP.NET Web API
- クライアント側:TypeScript、SpreadJSライブラリ
- IDE:Visual Studio 2015
作成する帳票Webアプリの図
対象読者
- ASP.NET、JavaScriptを利用する開発者
- スプレッドシートの機能に興味がある方
- Excelのような帳票Webアプリを作成したい方
必要な環境
本稿では、次の環境で開発・動作確認を行っています。
- Chrome
- Visual Studio 2015
- Windows 10
サンプルについて
サンプルにはSpreadJSライブラリは含まれておりません。ご利用いただく場合には、別途SpreadJSのトライアル版(または製品版)をダウンロードする必要があります。ダウンロードした後、サンプル内のREADME.txtに従ってファイルを配置してください。
既存のExcelファイルをSpreadJSに適用する
はじめに、帳票のデザインをSpreadJSで表示するにはどのようにしたらよいでしょうか。例えば、サンプルの請求書のデザインをSpreadJSに設定したいとします。

SpreadJSのデザインはJavaScriptの実装より行うことができますが、コード量が多くなり複雑になる可能性があります。そこで、すでにデザインされたExcelファイルを利用し、SpreadJSをあらかじめシリアル化しておくことで、アプリケーションの主となるロジックと区別することができます。
本稿のサンプルでは、製品ヘルプの「Excelのインポート機能」と「SpreadJSのシリアル化・逆シリアル化」を利用し、次の方法を選択しました。
- デザインされたExcelファイルをSpreadJSに読み込む。
- SpreadJSをシリアル化したJSONデータを「*.json」ファイルとして出力する。
- 作成した「*.json」ファイルをサーバー側に配置する。
- ブラウザ側で、初期表示に「*.json」ファイルの内容をSpreadJSに読み込む。
まず、(1)のExcelファイルをSpreadJSに読み込むための、クライアント側のTypeScriptの記述です。
// Excelファイルをインポートします
export function ImportExcelFile() {
let input: any = document.getElementById("fileDemo");
let excelFile = input.files[0];
excelIO.open(excelFile, function (json) {
var workbookObj = JSON.parse(json);
workbook.fromJSON(workbookObj);
}, e => { console.log(e); });
}
このクライアント側のExcelインポート機能については、連載第2回の「クライアントサイドのExcelインポート・エクスポート」でも紹介しておりますのでご参照ください。
次に、(2)のSpreadJS全体の設定をJSONデータとしてシリアル化するTypeScriptの記述です。
//SpreadJSの初期化
var workbook = new GC.Spread.Sheets.Workbook(document.getElementById("spreadjs-container"), {
sheetCount: 1
});
//JSONデータとして出力する
let json = JSON.stringify(workbook.toJSON({ includeBindingSource: false }));
詳細は、チュートリアルデモの「JSONによる初期化と保存」をご覧ください。
SpreadJSのシリアル化されたJSONデータは次のようになっています(一部のみ記載しています)。
{
"version": "9.20161.0",
"sheets": {
"Sheet1": {
"name": "Sheet1",
"frozenRowCount": 10,
"frozenTrailingRowCount": 12,
"rowCount": 33,
"columnCount": 4,
"activeRow": 4,
"activeCol": 2,
"spans": [
{
"row": 32,
"rowCount": 1,
"col": 0,
"colCount": 4
},
//..続く
以上の手順で取得したJSONデータを「*.json」ファイルとして出力し、サーバー側のASP.NET MVCプロジェクトの「App_Data/spread」フォルダ配下に配置します。

この「*.json」ファイルは、初期表示の際にサーバー側のAPIを通してクライアント側で取得し、SpreadJSに読み込みます。
「*.json」ファイルの中身を返すASP.NET Web APIの実装は次のようになります。
/// はじめに表示するSpreadJSのコンテンツを取得します
[HttpGet]
public HttpResponseMessage ReadSpread()
{
string fileName = "initialSpread.json";
string filePath = Path.Combine(spreadFolder, fileName);
var response = new HttpResponseMessage(HttpStatusCode.OK);
var fileStream = new FileStream(filePath, FileMode.Open);
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
}
注
ASP.NET Web APIで提供するAPIではなく、直接Webサーバー側のファイルを取得する方法でも構いません。ただし、*.jsonファイルが外部に公開されることに注意してください。またIISでは、規定では「*.json」ファイルの送信は許可されていませんので、MIMEマッピングを追加する必要があります。
クライアント側でAPIリクエストを送信し、「*.json」ファイルの中身を読み込み、SpreadJSに適用します。
function LoadWorkbook() {
//初期表示のSpreadJSを読み込みます
$.ajax({
url: "/api/spread/",
datatype: "json",
cache: false,
success: (data) => {
workbook.suspendPaint();
workbook.fromJSON(data);
let sheet = workbook.getSheet(0);
//シートに対してセットアップを行います。
SetupSheet(sheet);
workbook.resumePaint();
},
error: (ex) => {
console.log(ex);
}
} as JQueryAjaxSettings);
}
以上で、デザインされたExcelファイルをSpreadJSに適用できます。作成したExcelファイルはサンプル内にありますのでご参照ください。
データを保存する(1)
データの保存といえども、"とある値のみ保存する"のか、"帳票全体を保存"するのか等、決定しなければいけません。サンプルでは両方を実装していますが、まず、前者の"とある値のみ保存する"方法について紹介します。
管理するデータをJSONデータとして定義する
サーバー側で管理したいデータを帳票から切り出し、JSON形式のモデルとして定義します。例えば"請求書"では、次の図のように切り分けました。

赤枠がJSON形式のモデルとして定義する場所です。青枠は、Excelの数式のように、表示するときに計算される場所です。この青枠は、JSONモデルとして定義はしません(ただし、要件によっては、計算結果もサーバー側で管理することも考えられます)。
切り出したJSONモデルは次のようになります。
{
"publishDate": "2016-12-01",
"paymentDate": "2016-12-20",
"invoiceNumber": 10003,
"discount": 500,
"destination": {
"name": "杉本 昭 様",
"companyName": "杉本 昭 事務所",
"address1": "東京都",
"address2": "港区"
},
"publisher": {
"name": "山田 太郎",
"companyName": "山田 太郎 事務所",
"address1": "東京都",
"address2": "中央区"
},
"paymentInfo": {
"name": "山田 太郎",
"nameKana": "ヤマダタロウ",
"bankName": "銀行名",
"bankAccount": "1234567"
},
"items": [
{ "id": 1, "amount": 11, "name": "項目名1", "unitPrice": 1500, "sumPrice": 0 },
{ "id": 2, "amount": 13, "name": "項目名2", "unitPrice": 2500, "sumPrice": 0 },
//…続く
]
}
このようなJSONデータをサーバー側とAPIを通してやり取りすることにより、データの管理を行います。
SpreadJSにJSONデータを表示するには、「セルレベルバインディング」と「テーブルバインディング」の機能で実装します。バインディングにより、SpreadJSで編集した値は、クライアント側のObjectにも反映されます。詳細は、製品ヘルプの「データの連結」または連載第2回の「基本的な使い方(2)」をご参照ください。
次のコードは、バインディングの設定を行っているクライアント側のTypeScriptのコードです。JSONデータとSpreadJSの表示場所の関連付けを行い、テーブルを追加しています。テーブルはExcelファイルにて設定していても、Excelのインポート時に破棄されてしまうため、TypeScriptにて設定しています。
function BindSheet(sheet: GC.Spread.Sheets.Worksheet, data: any) {
let tableDataRowCount = data.items.length;
//請求書番号
sheet.setBindingPath(0, 1, "invoiceNumber");
//発行日
sheet.setBindingPath(1, 1, "publishDate");
//支払期日
sheet.setBindingPath(2, 1, "paymentDate");
//宛先
sheet.setBindingPath(4, 0, "destination.name");
sheet.setBindingPath(5, 0, "destination.companyName");
sheet.setBindingPath(6, 0, "destination.address1");
sheet.setBindingPath(7, 0, "destination.address2");
//発行者
sheet.setBindingPath(4, 2, "publisher.name");
sheet.setBindingPath(5, 2, "publisher.companyName");
sheet.setBindingPath(6, 2, "publisher.address1");
sheet.setBindingPath(7, 2, "publisher.address2");
//割引
sheet.setBindingPath(10 + tableDataRowCount, 3, "discount");
sheet.getCell(1, 1)
//支払情報
sheet.setBindingPath(16 + tableDataRowCount, 1, "paymentInfo.name");
sheet.setBindingPath(17 + tableDataRowCount, 1, "paymentInfo.nameKana");
sheet.setBindingPath(18 + tableDataRowCount, 1, "paymentInfo.bankName");
sheet.setBindingPath(19 + tableDataRowCount, 1, "paymentInfo.bankAccount");
//シートにテーブルをバインドします
let amountInfo = new GC.Spread.Sheets.Tables.TableColumn("数量");
amountInfo.name("数量");
amountInfo.dataField("amount");
let nameInfo = new GC.Spread.Sheets.Tables.TableColumn("項目");
nameInfo.name("項目");
nameInfo.dataField("name");
let unitPriceInfo = new GC.Spread.Sheets.Tables.TableColumn("単価");
unitPriceInfo.name("単価");
unitPriceInfo.dataField("unitPrice");
let sumPriceInfo = new GC.Spread.Sheets.Tables.TableColumn("金額");
sumPriceInfo.name("金額");
sumPriceInfo.dataField("sumPrice");
let table = sheet.tables.addFromDataSource("TableDetails", 9, 0, data.items, GC.Spread.Sheets.Tables.TableThemes.medium23);
table.bindColumns([amountInfo, nameInfo, unitPriceInfo, sumPriceInfo]);
//金額の列の計算式を設定します
table.setColumnDataFormula(3, "=[@単価]*[@数量]");
//小計の計算式を設定します
sheet.getCell(11 + tableDataRowCount, 3).formula("=SUM(TableDetails[金額])-Discount");
//シートにデータをバインディングします
sheet.setDataSource(new GC.Spread.Sheets.Bindings.CellBindingSource(itemData));
}
「数式」や「名前の定義」は、Excelインポートを行っても保持されるので、あらかじめExcelにて設定しておくとよいでしょう。サンプルでは、「割引」や「小計」「税」などに名前を設定しています。
このようなバインディング機能により、JSONデータとSpreadJSの表示を分離して開発を行うことができます。
データを保存する(2)
JSONデータの保存と読み込み
前節のように定義したJSONデータは、サーバー側から取得し、SpreadJSに表示して編集した後、サーバーに送信することで管理します。サンプルでは、JSONデータの保存方法として「サーバー側のローカルにてファイルを保存する」方法を選択しました。この他にも、サーバー側にてJSONデータを逆シリアル化し、Microsoft SQL ServerやRedisなど、さまざまな永続化の方法を選択できます。技術資料の「ASP.NET MVCでの使い方」では、ADO.NET Entity Data Modelでの実装があります。
次のコードは、サーバー側のASP.NET Web APIにて、初期データを提供する実装です。
/// <summary>
/// 初期表示に表示するデータ initial.json を取得します
/// </summary>
/// <returns></returns>
[HttpGet, Route("initial")]
public HttpResponseMessage GetInitialItem()
{
string fileName = "initialItem.json";
var filePath = Path.Combine(dataFolder, fileName);
var response = new HttpResponseMessage(HttpStatusCode.OK);
var fileStream = new FileStream(filePath, FileMode.Open);
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json);
return response;
}
JSONデータをクライアント側で読み込み、SpreadJSにバインディングします。
//シートのセットアップを行います
function SetupSheet(sheet: GC.Spread.Sheets.Worksheet) {
$.ajax({
url: "/api/item/initial",
datatype: "json",
cache: false,
success: data => {
//シートにデータをバインドします
BindSheet(sheet, itemData);
//列の自動生成をOFFにします
sheet.autoGenerateColumns = false;
//上下の行を固定します。
sheet.frozenRowCount(9);
sheet.frozenTrailingRowCount(12);
},
error: (XMLHttpRequest, textStatus, errorThrown) => {
console.log(errorThrown)
}
} as JQueryAjaxSettings);
}
例として、次のようにテーブルの行を編集します。

このとき、JavaScriptのオブジェクトも変更されている状態になります。

さらに続けて、テーブルに行を追加してみましょう。行を追加するコードは以下のようになります。この場合もJavaScriptのオブジェクトに1行追加された状態になります。
//SpreadJSの行を追加します
export function AddItem() {
var sheet = workbook.getActiveSheet();
//現在選択している行を取得します
var activeRowIndex = sheet.getActiveRowIndex();
//選択行の下に1行追加します。
sheet.addRows(activeRowIndex + 1, 1);
}
保存する際は、JSONデータをAPIリクエストとしてサーバー側へ送信します。
//データをuser1として保存します
export function SaveData() {
$.ajax({
url: "api/item/user1",
type: "POST",
data: JSON.stringify(itemData),
dataType: "json",
contentType: "application/json,charset=UTF-8"
});
}
サーバー側のASP.NET Web APIでは、送信されてきたJSONデータをファイルに保存しています。
/// データをJSONファイルとして、user1.json の名前で保存します
[HttpPost, Route("user1")]
public async Task Save()
{
string fileName = "user1.json";
var filePath = Path.Combine(dataFolder, fileName);
//リクエストのBodyからJSONデータを読み込みます
var stream = await Request.Content.ReadAsStreamAsync();
//省略
これまで説明してきたように、管理したいデータをJSONデータとして定義し、SpreadJSにはバインディングで表示を制御し、サーバー側とはAPIでやり取りすることによりデータを管理できます。また、同じような方法で、サーバー側にSpreadJSのシリアル化データを保存することで、帳票全体の管理が可能です。
サンプルでは実装していませんが、例えば、宛先や銀行情報はマスタデータから読み込むといったことも、JSONデータを操作するだけで実現できます。SpreadJSがサーバーと分離されたクライアントライブラリだからこそ、柔軟に扱うことができます。
印刷
SpreadJSでは印刷をサポートしています。詳細は製品ヘルプの「印刷の概要」をご参照ください。サンプルでは、HTMLのボタンを用意し、印刷機能を実装しました。
クライアント側の印刷の実装は次のようになります。
//印刷します
export function Print() {
let printSheetIndex = 0;
var info = workbook.getSheet(printSheetIndex).printInfo() as GC.Spread.Sheets.Print.PrintInfo;
info.showColumnHeader(GC.Spread.Sheets.Print.PrintVisibilityType.hide); info.showRowHeader(GC.Spread.Sheets.Print.PrintVisibilityType.hide);
info.showGridLine(false);
info.showBorder(false);
workbook.print(printSheetIndex);
}
下の図は、Chromeブラウザで印刷を実行したときの図です。

保存状態に関わらず、そのままのSpreadJSの状態で印刷を行うことができます。
最後に
連載の最終回である本稿では、実践的な内容として、"請求書"を例にした帳票Webアプリの作成をご紹介しました。
今回のポイントとなる機能は以下の通りです。
- クライアント側Excelインポート・エクスポート
- JSONデータとSpreadJSとのバインディング
- SpreadJSのシリアル化・逆シリアル化
- 印刷機能
- TypeScriptによる記述
これらのポイントと、サーバーとクライアントの分離を意識した設計をおさえると、柔軟性が高い、表機能のWebアプリに一歩近づきます。連載でご紹介した機能以外にも、さまざまな機能がありますので、ぜひSpreadJSサイトをご覧ください。

