はじめに
この記事は、ASP.NET MVCについての内容になります。
ASP.NET Coreには、そのまま利用できない情報となりますので、ご了承ください。
なぜスプレッドシートを採用するに至ったのか
とあるシステムへデータを投入するUIを作成することになったのですが、予想に反して茨の道でした。
投入されるデータは、エクセルでフォーマット化されておりCSVで保存したものをアップロードしてもらうという約束で、実装を進めていたのですが、上手く行かないという連絡が入ります。
もちろん、事前に送ってもらったサンプルデータを使って、プログラムに問題が無いことを確認している訳ですが、どうやらサンプルデータと本番データで、微妙にデータが異なっているようです。
必須カラムが欠落していたり、行によってカラム数が異なっているなど、CSVとして不正なものでした。
エンドユーザーに開放する機能では無く、管理者のみが使う機能で、費用を掛けられないため本格的なエラーチェック機構を作り込むのも難しい状況だったため、メールの往復を何度も行って、データを少しづつ手直ししました。
行数とカラム数でいうとかなり大きなファイルになるため骨の折れる作業でした。
システム屋的には、事前に交わしたフォーマット以外は、受け付けないと言いたいところですが、相手のリテラシーに合わせて設計を考える必要があります。
ユーザーサイドも根気よく修正に対応していただき機能するようになったのですが、「エクセルデータを投入するようなユースケースでは、CSVアップロードは、避けるべき」という教訓が残りました。
もちろん、プログラマー向け機能とかなら使えると思うのですが、データ構造などに普段触れられていない方達向けに使用すべきではありませんでした。
時は流れて数年後
ASP.Net Coreへのマイグレーションを考えなくてはいけない時期ですが、機能追加の要望がユーザーからありました。
またしても連続データ投入を行う機能です。
同じ轍を踏む訳には行きません。
何を選ぶか
タイトルでネタバレしているため最終的に、スプレッドシートを採用する訳ですが、他にも選択肢はあると思います。
確認ページを挟んだり、バリデーションを強化するのも良さそうです。
しかし、Web画面のUIとして、スプレッドシートを入力欄として使用することができるようになると、今後の応用範囲が大きそうだということで、スプレッドシートを採用しました。
一般の方でもエクセルは使い方分かるという方が非常に多いため、逆に「エクセルみたいに入力できませんか?」と言われることも多いんですよね。
jExcel CE
ネットで検索すると色々なライブラリがあるようです。
その中でもベンダーロックインせずに、使えるjExcel CEというライブラリを使用することにしました。
https://bossanova.uk/jexcel/v3/
実装
表示
jExcelを画面に表示するだけなら非常に簡単です。
ドキュメントに従って、配置をしてください。
<script src="https://bossanova.uk/jexcel/v4/jexcel.js"></script>
<script src="https://bossanova.uk/jsuites/v3/jsuites.js"></script>
<link rel="stylesheet" href="https://bossanova.uk/jsuites/v3/jsuites.css" type="text/css" />
<link rel="stylesheet" href="https://bossanova.uk/jexcel/v4/jexcel.css" type="text/css" />
<div id='my-spreadsheet'></div>
<script>
data = [
['Mazda', 2001, 2000],
['Pegeout', 2010, 5000],
['Honda Fit', 2009, 3000],
['Honda CRV', 2010, 6000],
];
jexcel(document.getElementById('my-spreadsheet'), {
data:data,
columns:[
{ title:'Model', width:300 },
{ title:'Price', width:80 },
{ title:'Model', width:100 }
]
});
</script>
サブミット
jExcelは、ASP.NETで提供されているコンポーネントではありませんので、当然ながらRazor文法によるサブミットは、利用できません。
Ajaxを使用してサブミットをインターセプトして、jExcelのデータを通信へ載せてやる必要があります。
隠しフィールドへJSONデータを上書きすることで、実現しました。
<div id="spreadsheet"></div>
@using (Html.BeginForm("Save", "EntryData", FormMethod.Post, new { id = "entry-data" }))
{
@Html.AntiForgeryToken()
<div class="form-row">
<div class="input-group col-sm-6">
@Html.HiddenFor(model => model.JsonData)
</div>
</div>
<input type="submit" class="btn btn-primary" value="保存">
}
<script>
// サブミットボタンをハイジャックする
$("#entry-data").submit(function (e) {
// イベントをキャンセル
e.preventDefault();
// jExcelデータをJSONへエクスポート
var tableData = table1.getData();
var json = JSON.stringify(tableData);
// 隠しフィールドへJSONを設定
var hiddenForm = document.getElementById('JsonData');
hiddenForm.value = json;
// 操作対象のフォーム要素を取得
var $form = $(this);
// 送信ボタンを取得
var $button = $form.find('button');
// 送信
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: $form.serialize(),
// 送信前
beforeSend: function (xhr, settings) {
// ボタンを無効化し、二重送信を防止
$button.attr('disabled', true);
},
// 応答後
complete: function (xhr, textStatus) {
// ボタンを有効化し、再送信を許可
$button.attr('disabled', false);
},
// 通信成功時の処理
success: function (result, textStatus, xhr) {
// 入力値を初期化
$form[0].reset();
},
// 通信失敗時の処理
error: function (xhr, textStatus, error) {
alert('送信に失敗しました。');
}
});
});
</script>
バリデーション
jExcelは、セルに着色やコメントを表示する機能がありますので、ユーザーに対してピンポイントで、チェック結果を伝えることができます。