【Handsontable】変更マークの付与と必須チェック

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

前回は「[【Handsontable】全選択/全解除チェックと行削除](https://qiita.com/yaju/items/5acbb061baa3a495cf8e)」では全選択/全解除チェックと行削除を行いました、今回は変更マークの付与と必須チェックを実装していきます。


# 変更マークの付与
先頭列(Edit)に同じ行のいずれかの列の変更があった場合に「*」を付ける。
但し、編集列と選択列は対象外とする。

Handsontableではデータの更新時、afterChangeオプションに指定したメソッドが呼び出される。

```javascript
const EDIT_MARK = '*';

// changesには[[row, col, pretext, aftertext]] でセットされる。
afterChange: function (changes, source) {
    if (source === 'loadData') return;

    for (var i = 0; i < changes.length; i++) {
        var change = changes[i];
        // 編集と選択は対象外
        if (change[1] === COL_EDIT || change[1] === COL_SELECT) continue;
        // 変更前と変更後が同じは対象外
        if (change[2] === change[3]) continue;

        // 編集に"*"を付ける
        hot.setDataAtCell(changes[0][0], 0, EDIT_MARK);
    }
}
```

# 必須チェック
必須チェックする項目としては、商品コードと商品名と単価となります。

値をチェックするカラムに下記のvalidatorオプションを追加します。
validator: function (value, callback) { callback(true); }

```javascript
var hot = new Handsontable(grid, {
    columns: [
        { data: COL_EDIT, readOnly: true, type: 'text' },
        { data: COL_SELECT, type: 'checkbox' },
        { data: COL_PRODUCTCODE, type: 'text', width: 80, validator: function (value, callback) { callback(true); } },
        { data: COL_PRODUCTNAME, type: 'text', width: 200, className: "htLeft htMiddle", validator: function (value, callback) { callback(true); } },
        { data: COL_UNITPRICE, type: 'numeric', numericFormat: { pattern: '0,00', culture: 'ja-JP' }, validator: function (value, callback) { callback(true); } },
        { data: COL_COMMENT, type: 'text', width: 300, className: "htLeft htMiddle", validator: function (value, callback) { callback(true); } }
    ],
```

すると、afterValidateオプションに指定したメソッドが呼び出されます。
※カラムにvalidatorオプションが必要だったかちょっと分からなくなったのですが、ないとメソッドが呼ばれない。

【2018/12/16追記】過去の自分用のメモには下記のように書いてありました。
>text入力で afterValidateでまとめて桁数チェックをしようとしたが、afterValidateが動作しない。
無理矢理、validatorを経由させることでafterValidateの処理に入ってくるようになった。
※IE11対応なのでアロー演算子は使用していない。


今回は、先頭列に編集マークがある場合のみ必須項目チェックを行います。
こうすることで新規行はチェックされないようになります。
※編集マークがなければ beforeValidateオプション でもいいと思う。

```javascript
afterValidate: function (isValid, value, row, prop, source) {
    // 既にエラーなら何もしない
    if (!isValid) return false;

    // 桁数チェック
    switch (prop) {
        case COL_PRODUCTCODE:
            // 必須項目チェック
            if (this.getDataAtCell(row, 0) === EDIT_MARK && (value === null || value.trim() === "")) return false;
            // 桁数チェック
            if (value !== null && value.length !== 5) return false;
            break;
        case COL_PRODUCTNAME:
            // 必須項目チェック
            if (this.getDataAtCell(row, 0) === EDIT_MARK && (value === null || value.trim() === "")) return false;
            // 桁数チェック
            if (value !== null && value.length > 30) return false;
            break;
        case COL_UNITPRICE:
            // 必須項目チェック
            if (this.getDataAtCell(row, 0) === EDIT_MARK && (value === null || value === "")) return false;
            break;
        case COL_COMMENT:
            if (value !== null && value.length > 100) return false;
            break;
    }

    return true;
}
```
桁数が不一致やオーバーしていたり値が入っていない場合、不正な項目の背景色が赤くなります。
![必須チェック.png](https://qiita-image-store.s3.amazonaws.com/0/14053/274aed50-1c77-776e-d5a0-ec7bf7dd8371.png)