【Handsontable】カレンダーの日本語化とセルにカスタムHTML表示など

# はじめに
これは、[Handsontable Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handsontable)の25日目の記事となります。

* [【Handsontable】導入と設定](https://qiita.com/yaju/items/c0307fa9ea69ad02c189)
* [【Handsontable】データの読み込みと行追加](https://qiita.com/yaju/items/3d7f598867556077dac5)
* [【Handsontable】全選択/全解除チェックと行削除](https://qiita.com/yaju/items/5acbb061baa3a495cf8e)
* [【Handsontable】変更マークの付与と必須チェック](https://qiita.com/yaju/items/900cef462f0daf69c19b)
* [【Handsontable】最大文字数制限(MaxLength)](https://qiita.com/yaju/items/16dc38684314157a1736)
* [【Handsontable】確定ボタンのチェック処理](https://qiita.com/yaju/items/b1b6a5deea8a67f0d0be)
* [【Handsontable】ドロップダウンと変更イベント](https://qiita.com/yaju/items/c0b1a58b4cf5a6885935)
* [Handsontable for Vueの紹介](https://qiita.com/yaju/items/6fabe92923261d5719ab)

# カレンダーの日本語化
![hotCalendar.png](https://qiita-image-store.s3.amazonaws.com/0/14053/809814f0-ff76-f2d8-06f4-10a93f1c2a6e.png)

Handsontableのカレンダー表示は、内部では [Pikaday.js](https://github.com/Pikaday/Pikaday) を使用している。
カレンダー設定は、datePickerConfigオプションで細かな変更が可能。
例えば、firstDayを1にすると月曜始まりのカレンダーとなる。工場を持つ企業などで使われている。※各設定の意味は、[PikaDay.jsメモ](https://38elements.github.io/2014/02/02/pikadayjs.html) を参考にする。

```javascript
datePickerConfig: {
    // First day of the week (0: Sunday, 1: Monday, etc)
    firstDay: 1
}
```

カレンダーの日本語化するには、NuGetにて「Moment.js」をインストールする。
「moment.locale('ja');」を実行すれば、monthsとweekdaysとweekdaysShortは、Momentから取得できる。

```html
<script src="../Scripts/moment-with-locales.min.js"></script>
```

```javascript
// 日本語化
moment.locale('ja');

var hot = new Handsontable(grid, {

columns: [
    { data: COL_SHIPMENTDATE, type: 'date', width: 150, 
            dateFormat: 'YYYY/MM/DD',
            datePickerConfig: {
            yearSuffix: '年',
            showMonthAfterYear: true,
            showDaysInNextAndPreviousMonths: true,
            i18n: {
                previousMonth: '前月',
                nextMonth: '次月',
                months: moment.localeData()._months,
                weekdays: moment.localeData()._weekdays,
                weekdaysShort: moment.localeData()._weekdaysShort
            }
        },
        validator: function (value, callback) { callback(true); }
    },

```

## 土日の色を変更する
CSS3の擬似クラスを使用して、色を変更する。handsontable用のcssファイルに含めて置き換えてもいいし、外出しにしてもいい。
[何番目系の便利なCSSまとめ](https://qiita.com/ituki_b/items/62a752389385de7ba4a2)

```css:日曜始まり
.pika-lendar th:first-child,
.pika-lendar td:first-child .pika-button {
    color: #f00;
}
.pika-lendar th:last-child,
.pika-lendar td:last-child .pika-button {
    color: #00f;
}
```

```css:月曜始まり
.pika-lendar th:nth-last-child(2),
.pika-lendar td:nth-last-child(2) .pika-button {
    color: #00f;
}
.pika-lendar th:last-child,
.pika-lendar td:last-child .pika-button {
    color: #f00;
}
```

[Pikaday.js 祝日対応とMoment不使用フォーマット整形](https://qiita.com/akuden/items/b26bf6880c8352888945)

# セルにカスタムHTML表示
HTMLを表示できるので、リンクや画像を表示できる。
[Rendering custom HTML in cells](https://www.connydado.at/inc/jquery/handsontable-master/demo/renderers_html.html)
![hotCellCustom.png](https://qiita-image-store.s3.amazonaws.com/0/14053/b3412a11-1a3f-116d-0ca7-88dc18e24a03.png)

## セルにボタン表示
標準ではボタンは用意されていないが、HTMLを表示出来ることを利用する。
http://jsfiddle.net/handsoncode/zram5uaL/
https://jsbin.com/heqijexeci/1/edit?html,js,output

DateTimePickerの時間を組み合わせて、ボタンを押したら時間入力ダイアログを表示して時間をセットする。
![StartTime.png](https://qiita-image-store.s3.amazonaws.com/0/14053/ebde57d4-1604-a0ae-6135-5ede0ddca95c.png)
![inputTime.png](https://qiita-image-store.s3.amazonaws.com/0/14053/787cf97d-e8cd-3c94-7286-546ba93ba2b5.png)
[Bootstrap 4でDatetimePickerを使いたい](https://qiita.com/yaju/items/2cbe5e5914c5be08820a)

```javascript
$('#datetimepicker1').on('change.datetimepicker', function(e) {
    if(selectedCol === 0) return;
    hot.setDataAtCell(selectedRow, selectedCol, e.date.format("HH:mm"));
});
 
function myBtns(instance, td, row, col, prop, value, cellProperties) {
    Handsontable.renderers.TextRenderer.apply(this, arguments);
    if (value === null) value = "00:00";
    td.innerHTML = '<button class="myBtn bt-' + row + '">' + value + '</button>'
}
 
var  hot = new Handsontable(grid, {
 
    cells: function(row, col) {
        var cellPrp = {};
        if (col === 5) {
            cellPrp.renderer = myBtns;
            cellPrp.readOnly = true;
        }
        return cellPrp
    },
    afterOnCellMouseDown: function(event, cords, TD) {
        if (event.realTarget.className.indexOf('myBtn') < 0) {
            return;
        }
 
        var time = hot.getDataAtCell(selectedRow, selectedCol);
        if (time === null) time = "00:00";
        $('#datetimepicker1').data("datetimepicker").defaultDate(moment(moment().format('L') + ' ' + time));
        $('#timeModal').modal();
    }
    afterSelection: function (r, c, r2, c2) {
        // ボタンとかおしてフォーカスがGridから離れると
        // getSelected() では取れなくなるので、
        // このタイミングで常に保持しておいたほうが無難
        selectedRow = r2;
        selectedCol = c2;
    }
 
}
```

# 列の非表示
列の非表示はPRO版では実装されています。

フリー版で実現する際は特定の列の幅を0.1ピクセルにして実現させます。
技術的にはテーブルの一部であり、getData() は列のデータを返しますが、人間の目には見えません。
※IE11では、チェックボックスにしていたためかセルが表示されてしまう不具合があります。

但し、セル移動した際に非表示列にもフォーカスが移動してしまいます。
![hotHiddenColumn.png](https://qiita-image-store.s3.amazonaws.com/0/14053/2c4f368f-7b23-3703-d5ab-f77342dec870.png)


```javascript
var hot = new Handsontable(grid, {

columns: [
    { data: "DBRead", type: 'text', width: 0.1 }
```

  * [handsontable: hide some columns without changing data array/object](https://stackoverflow.com/questions/28793529/handsontable-hide-some-columns-without-changing-data-array-object)
  * [Handsontableで列を非表示にする(非表示列を持たせる)](http://j-levia.hatenablog.jp/entry/2018/06/25/183241)
  * [Hiding columns PRO版](https://docs.handsontable.com/pro/5.0.0/demo-hiding-columns.html)

## 非表示列の値の取得
バインドしてある前提で、幅を 0.1 にしなくても非表示列の値が取得できる方法があります。※この際に非表示列を設定する必要はありません。

```javascript
var hot = new Handsontable(grid, xxx);

if (hot.getSourceDataAtCell(row, "DBRead") === true)
```

※DBから読んだデータと新規入力のデータと分ける上で DBRead列が true で判定する場合を想定。

[getSourceDataAtCell returns same as getDataAtCell](https://github.com/handsontable/handsontable/issues/5318)

# Jsonデータのまま保存する
Jsonデータを loadData メソッドで Handsontable でデータにロードした場合、データを保存する時にもJsonデータのままサーバー側に渡したい場合、getData ではなく getSorceData を使用する。

[Posting Handontable JSON using Ajax to ASP.net MVC controller not working Since "0.22.0"](https://github.com/handsontable/handsontable/issues/3368)

## getSourceDataとgetDataの違い
  * getSourceData() で取得したデータは元となったデータ src と同じインスタンスを指している。
  * 一方で、 getData() で取得したデータは、元のデータとは別のインスタンスになっている。

[getSourceData：ソースデータをそのまま取得する](https://qiita.com/opengl-8080/items/e756af444147771fab18#getsourcedata%E3%82%BD%E3%83%BC%E3%82%B9%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%81%9D%E3%81%AE%E3%81%BE%E3%81%BE%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)

# リサイズでサイズ変更する
リサイズ後にサイズを変更する。
リサイズ後(resized)イベントは、下記サイトを参考
[JavaScript で window リサイズ終了後のタイミングをハンドリングする方法](https://dev.classmethod.jp/client-side/handling-resized-event/)

Bootstrapのグリッドシステムを採用している場合、divタグに"id=Box"属性を付けて横幅を取得する。
その横幅を少し小さめにHandsonTableのwidthにセットしている。そうしないと縦スクロールバーが見えなくなってしまう。
縦幅は最低限表示するサイズ(150)と、最大で表示するサイズ(400)を指定している。

```javascript
var resizeTimer;
var interval = Math.floor(1000 / 60 * 10);

// リサイズ完了時
window.addEventListener('resize', function (event) {
    if (resizeTimer !== false) {
        clearTimeout(resizeTimer);
    }
    resizeTimer = setTimeout(function () {
        resize();
    }, interval);
})

// リサイズ
function resize() {
    var client_w = document.getElementById('box').clientWidth;
    var client_h = window.parent.screen.height;
    //alert("Resized client_w:" + client_w + " x client_h:" + client_h);

    hot.updateSettings({
        width: client_w - 20,
        height: Math.min(Math.max(150, client_h - 440), 400)
    });
}
```

# その他
  * [Qiita handsontable tags](https://qiita.com/tags/handsontable/items)
  * [handsontable 　行ごとの書式設定について（数値とパーセンテージの切り替え）](https://developer.cybozu.io/hc/ja/community/posts/115019266283-handsontable-%E8%A1%8C%E3%81%94%E3%81%A8%E3%81%AE%E6%9B%B8%E5%BC%8F%E8%A8%AD%E5%AE%9A%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-%E6%95%B0%E5%80%A4%E3%81%A8%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%86%E3%83%BC%E3%82%B8%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88-)
  * [Handsontable で callback サンプル(消費税計算)](http://oboe2uran.hatenablog.com/?page=1400503996)
  * [HandsontableのEditorManagerを使ってみた](https://anz-note.tumblr.com/post/133670437421/jshandsontable%E3%81%AEeditormanager%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F)
  * [Handsontableで、dropdown　の値を、セル変更後に変更する](http://blog.livedoor.jp/kmiwa_project/archives/1066567598.html)
  * [Handsontableを使ってkintoneをExcelライクに入力しよう その1](https://developer.cybozu.io/hc/ja/articles/206959893-Handsontable%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6kintone%E3%82%92Excel%E3%83%A9%E3%82%A4%E3%82%AF%E3%81%AB%E5%85%A5%E5%8A%9B%E3%81%97%E3%82%88%E3%81%86)
  * [Handsontableを使ってkintoneをExcelライクに入力しよう その2](https://developer.cybozu.io/hc/ja/articles/207331836-Handsontable%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6kintone%E3%82%92Excel%E3%83%A9%E3%82%A4%E3%82%AF%E3%81%AB%E5%85%A5%E5%8A%9B%E3%81%97%E3%82%88%E3%81%86-%E3%81%9D%E3%81%AE2)
  * [Handsontableで本格的な帳票の入力フォームを作るとき必要だったことたち](https://qiita.com/kawanakashotaro/items/74593b914b29b577e355)
  * [Handsontableのヘッダーをカスタマイズして複数行表示や結合に対応](https://qiita.com/Akashi26/items/9291efbb028457ae6a33)
  * [ASP.NET MVCのHandsontableデータの読み込みと保存](https://www.codeproject.com/Tips/1074230/Load-and-Save-Handsontable-Data-From-To-Controller)
  * [Web上でExcelライクなエディタ「Handsontable」](https://qiita.com/CS_KOGANE/items/ed27ff0a28b7ee045b9d)
  * [Handsontable 入力バリデーションの捕捉](http://oboe2uran.hatenablog.com/entry/2014/05/19/215316)
  * [HandsonTableで偽EXCELやってみた](http://ujimasayuruyuru.blogspot.jp/2016/12/handsontableexcel.html)
  * [HandsonTableのサンプル改良](http://ujimasayuruyuru.blogspot.jp/2016/12/handsontable.html)
  * [WEBでEXCELのような表を作成！Handsontableを使ってみよう！](http://www.go-next.co.jp/blog/web/javascript/17934/)
  * [Handsontable　で ajax でデータを取得・更新](http://blog.livedoor.jp/kmiwa_project/archives/1064081987.html)
  * [Googleスプレッドシートのようなものをお手軽につくれちゃうHandsontable](https://anz-note.tumblr.com/post/133074040041/jsgoogle%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE%E3%82%92%E3%81%8A%E6%89%8B%E8%BB%BD%E3%81%AB%E3%81%A4%E3%81%8F%E3%82%8C%E3%81%A1%E3%82%83%E3%81%86handsontable)
  * [選択セルの背景色を変更する](https://www.cnblogs.com/QiuJL/p/6957751.html)

# 最後に
[Handsontable Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handsontable) の最後の記事でした。
お読み頂いた皆様ありがとうございます。
使いこなすにはあまりある機能満載なライブラリーなので、また記事を増やしていきたいと思っています。