Edited at

【Handsontable】導入と設定


はじめに

これは、Handsontable Advent Calendar 2018の1日目の記事となります。

お仕事でWeb開発をした際にグリッド入力が必要で、以前ネット記事で見たJavascript用グリッドライブラリの「Handsontable」を使用することにしました。

その開発で培ったTipsをAdvent Calendarを利用して書いていきます。

フリーで商用利用可能なグリッドライブラリ - しぐれがきのブログ

数あるJavascript用のフリーのグリッドライブラリの中でも使い勝手が一番いい。

【2019/03/28追記】


Handsontable7.0.0は6.2.2と大きく変わりました。

まずHandsontable ProはHandsontable Community Editionと統合されてHandsontableは1つだけになりました。(中略)

MITライセンスではなくなり、非商用および評価版のみ無償のライセンスになりました。

Handsontable 7.0.0を使う人へ


Handsontable6.2.2以前のバージョンならMITライセンスが適用されます。


最近、Handsontableのライセンスモデルについて長い間熱心に話し合っていました。私たちは、オープンソースのライセンスソフトウェアに基づいて持続可能なビジネスを構築することが非常に困難になっていることに気付きました。(中略) Handsontable Proから得たお金のおかげで走り続けました。残念ながら、コマーシャルとフリーユーザーの比率は1から25です。したがって、フリーユーザーへの投資を継続する唯一の方法は、より多くのフリーユーザーを有料に変換することです。Handsontableを継続的に向上させることが唯一の使命である企業として成長したいです。

Handsontable drops open source for a non-commercial license



Handsontableとは

「Handsontable(ハンズオンテーブル)」は、WEBでExcelのようなスプレッドシートライクな入力を可能にしてくれるJavaScriptライブラリです。

ポーランドに会社がある「Handsontable」が開発しています。

公式サイト:https://handsontable.com/

ライセンスは、MITとなっており、有料版「Handsontable Pro」があります。

デモで見るとわかりますが、Excelのようにデータ入力ができるだけでなく、セルの書式の指定やチャートが作れたりと、機能が多いのも魅力です。


必要パッケージ導入

下記サイトに導入方法が提示されています。

https://handsontable.com/download?version=ce

handsontableは公式でVue.js向けのラッパーを出しています。

Vue.jsでhandsontableを使う

Handsontable for Vueの紹介

今回はVisual Studio 2015による開発だったので NuGetでインストールしました。


使い方


サンプル

今回はExcelのような自由な入力形式ではなく、一般的なグリッド入力を想定します。

例として、簡易的な商品マスタを作成および改良していきます。

商品マスタ.png


仕様

項目
内容

全選択/全削除
選択欄の全てをチェックオン/オフにします。

行追加
最下行に行追加します。

確定
商品マスタを保存します。

行削除
選択欄のチェックオンを行削除します。

編集
新規に編集した行に「*」マークを付けます。編集不可

選択
行削除する行を選択します。

商品CD
商品コードを入力します。5桁

商品名
商品名を入力します。30桁

単価
単価を入力します。

備考
備考を入力します。100桁


実装

NuGetで下記ライブラリーをインストールしています。


  • bootstrap v4.1.3

  • FontAwesome v4.7.0

  • Handsontable v6.2.0

  • jQuery v3.3.1


product-master.html

<!DOCTYPE html>

<html>
<head>
<meta charset="UTF-8" />
<title>Handsontable Example</title>
<link rel="stylesheet" href="../Content/bootstrap.min.css">
<link rel="stylesheet" href="../Content/handsontable/handsontable.full.min.css" />
<link rel="stylesheet" href="../Content/font-awesome.min.css">
<link rel="stylesheet" href="../Content/product-master.css">
</head>
<body>
<div class="card-body bg-custom">
<div class="container">
<div class="row">
<div class="title-custom">商品マスタ</div>
</div>
<div class="row">
<span class="pr-4"></span>
<label class="btn btn-default btn-custom"><input type="checkbox" id="allCheck" onclick="allCheck(this)"><span class="pr-2"></span>全選択/全削除</label>
<button type="button" class="btn btn-labeled btn-info btn-custom" onclick="addRow()">
<span class="btn-label"><i class="fa fa-edit"></i></span>行追加
</button>
<button type="button" class="btn btn-labeled btn-success btn-custom" onclick="register()">
<span class="btn-label"><i class="fa fa-check-circle"></i></span>確 定
</button>
<button type="button" class="btn btn-labeled btn-danger btn-custom" onclick="delRow()>
<span class="
btn-label"><i class="fa fa-trash-o"></i></span>行削除
</button>
</div>
<div class="row">
<div id="grid" class="ml-3"></div>
</div>
</div>
</div>
<script src="../Scripts/handsontable/handsontable.full.min.js"></script>
<script src="../Scripts/jquery-3.3.1.min.js"></script>
<script src="../Scripts/product-master.js"></script>
</body>
</html>


product-master.css

.title-custom {

padding: 0px 0px 10px 0px;
font-size:20px;
font-weight: bold;
}

.btn-custom {
margin-right: 20px;
}

.btn-label {
position: relative;
left: -12px;
display: inline-block;
padding: 6px 12px;
background: rgba(0,0,0,0.15);
border-radius: 3px 0 0 3px;
}

.btn-labeled {
padding-top: 0;
padding-bottom: 0;
}

.btn {
margin-bottom: 10px;
}

.handsontable th,
.handsontable td {
padding: 2px 10px 2px 10px;
font-size: 16px;
text-align: center;
}

.handsontable th:last-child {
padding-left: 8px;
text-align: left;
}

.handsontable td:first-child {
background: #EEE;
}



product-master.js

var data = [

['', false, 'S0001', 'りんご', 100, '青森産'],
['', false, 'S0002', 'みかん', 80, '静岡産'],
['*', false, 'S0003', 'メロン', 1000, '袋井クラウンメロン']
];

var grid = document.getElementById('grid');

var hot = new Handsontable(grid, {
data: data,
colHeaders: ['編集', '選択', '商品CD', '商品名', '単価', '備考'],
columns: [
{ readOnly: true, type: 'text' },
{ type: 'checkbox' },
{ type: 'text' , width: 80 },
{ type: 'text' , width: 200, className: "htLeft htMiddle" },
{ type: 'numeric', numericFormat: { pattern: '0,00', culture: 'ja-JP' }},
{ type: 'text' , width: 300, className: "htLeft htMiddle" }
],
enterMoves: { row: 0, col: 1 },
outsideClickDeselects: true,
manualColumnResize: true,
fillHandle: false
});



説明

Handsontable 用に幾つか設定したグリッドオプションについて説明していきます。


enterMoves:Enterキーによるセルの横移動の設定

Enter でセルを抜ける時、デフォルトだと下のセルに移動するようになっているため、横に移動するようにします。ちなみに Tab でセルの横に移動できます。

enterMoves: { row: 0, col: 1}


outsideClickDeselects:表外をクリックしたら選択状態を解除させる

outsideClickDeselects: true

デフォルトは true で、表外をクリックすると選択状態が解除されます。

選択状態を残した状態にするなら、 false にします。


manualColumnResize:列の幅をドラッグで変更できるようにする

manualColumnResize: true

列ヘッダーを表示させた状態の時に、列の幅をドラッグで変更できるようにしています。

ダブルクリックすれば、セルの内容に合わせて幅を最小サイズになります。

行のサイズ変更は、manualRowResize を使えばできます。


fillHandle:フィルハンドルの無効

fillHandle: false

フィルハンドルは選択したセルの右下にある小さい四角の形のもので、カーソルをドラッグすることで、セルの内容を上下 or 左右にコピーできる機能です。

今回は不要なので無効にしています。


文字配置

.handsontable th,

.handsontable td {
padding: 2px 10px 2px 10px;
font-size: 16px;
text-align: center;
}

デフォルトはヘッダー部は中央寄せ、データ部は左寄せになっているが、CSSを使用してヘッダー部とデータ部を中央寄せと余白を追加している。

データ部はカラム属性にて、className: "htLeft htMiddle" で左寄せの指定ができる。

className のアライメントの指定は下記の値を使用する。


  • Horizontal: htLeft, htCenter, htRight, htJustify,

  • Vertical: htTop, htMiddle, htBottom

ヘッダー部にカラム属性は見当たらないので、末尾の備考欄のみを左寄せにしたい場合は、下記のようにCSSの疑似クラスを使用して左寄せにすることができます。

.handsontable th:last-child {

padding-left: 8px;
text-align: left;
}


読み取り専用セル

編集欄は読み取り専用であるため、カラム属性にてreadOnly: true を設定しています。

読み取り専用にすると文字色は読み取り専用色に変更になりますが背景色は何も変わらないため、今回はCSSの疑似クラスを使用して先頭列の背景色を変更しています。

.handsontable td:first-child {

background: #EEE;
}

Custom renderers で行う方法はまた別の記事で紹介します。


最後に

これから記事に即して、少しずつ商品マスタを完成に近づけていきます。