IM-FormaDesignerのグリッドテーブルでチェックボックスを使う

# 対象となる開発形態
- intra-mart Accel Platform
   - IM-FormaDesigner

# やりたいこと
IM-FormaDesignerのグリッドテーブルアイテムでは、列の入力タイプにチェックボックスを使用することができないため、疑似的にチェックボックスを利用可能にする。

# 実現方法
- チェックボックスは列タイプ「カスタム」で、タグを直書きして作成する。
- 列タイプ「カスタム」ではデータ内容がデータベースに保存できないため、チェックボックスのデータを保持する隠しパラメータ列を用意する。
- スクリプトアイテムを使用し、チェックボックスの変更で隠しパラメータを更新する。
- アクション設定-初期表示イベントで、編集・参照時のチェックボックス状態を登録済みデータの隠しパラメータから設定する。

## チェックボックスが1つの場合
### カスタム列(チェックボックス本体)
「表示したいコードを記述してください。」の部分に以下のタグを設定します。

```html
<input type="checkbox" name="gt1_grid_check" value="1">
```
`value`属性にはチェック時にデータベースに登録したい値を設定し、`name`属性を設定することでスクリプトから取得可能にしておきます。

### 隠し項目列(データベース接続用)
列タイプ「カスタム」ではデータベース関連の設定がないため、チェックボックスの内容がデータベースに保存できません。
このため、別に列タイプ「隠しパラメータ」の列を追加して、こちらでデータベース内容を保持するようにします。
「フィールド識別ID」にはチェックボックスのデータを保存するデータベースフィールド名を設定し、「フィールド値DB登録」のチェックはオンにします。

### スクリプトアイテム
カスタム列の入力内容を隠し項目に保存するスクリプトを、スクリプトアイテムで作成します。

スクリプトアイテムを画面上に配置するか、アクション設定-初期表示イベントでカスタムスクリプトを追加し、以下のスクリプトを貼ります。

```js
// カスタム列のチェックボックスにchangeイベントを設定
$('table#gt1').on('change', 'input[name="gt1_grid_check"]', function() {
   // グリッドテーブルのIDを取得
   let tableid = $(this).closest('table')
                        .attr('id');
   // クリックされた行IDを取得
   let rowid = $(this).closest('tr')
                      .attr('id');

   // IM-BISクライアントサイドスクリプトAPI
   let args = {};
   args.data = {};
   args.data[tableid] = [];
   args.data[tableid][0] = {};

   // クリックされた行のみ値を設定
   let option = {};
   option.indexKey = rowid;

   let isChecked = $(this).prop('checked');
   if (isChecked) {
      // チェックされた場合、隠し項目列にvalue値を設定
      args.data[tableid][0]['gt1_hidden1'] = $(this).val();
   } else {
      // チェックを外した場合、隠し項目列の値をクリア
      args.data[tableid][0]['gt1_hidden1'] = '';
   }
   // グリッドテーブルに値を設定
   formaItems.product_80_gridtable.setItemData[tableid](args, option);

   // チェックボックスをオンにする
   $('table#' + tableid).find('tr#' + rowid)
                        .find('input[name="gt1_grid_check"]')
                        .prop('checked', isChecked);
});
```

グリッドテーブルに値を設定するAPIについては、以下を参照してください。
[IM-BIS for Accel Platform IM-BIS 仕様書 13. クライアントサイドスクリプトAPI](https://www.intra-mart.jp/document/library/bis/public/bis_specification/texts/spec/csjs_script.html)

注意点は、クライアントサイドAPIを実行した際にグリッドテーブルの再読み込みが走るらしく、チェック状態がクリアされてしまうことです。
このため、API実行後にスクリプトでチェックボックスをオンにする処理をもう一度実行しています。
なお、`change`イベントは追加要素にバブリングさせるためにグリッドテーブル`gt1`に追加しています。

### 初期表示イベント
最後に、編集や参照で登録済みデータを表示した時にチェックボックスの状態を正しく表示するため、アクション設定-初期表示イベントで隠し項目列の状態からチェックボックスのチェック状態を変更させるようにします。
アクション種別はカスタムスクリプトで、以下のコードを貼ります。

```js
// IM-BISクライアントサイドスクリプトAPI
var tableid = 'gt1'
var rowId = '';
var inputIdList = ['gt1_hidden1'];
// グリッドテーブルから値を取得
var result = formaItems.product_80_gridtable.getItemData[tableid](rowId, inputIdList);
var rowid;
for (var i = 0; i < result.length; i++) {
   rowid = i + 1;
   // 取得した値からチェックボックスをオンにする
   if (result[i].gt1_hidden1) {
      $('table#' + tableid).find('tr#' + rowid)
                           .find('input[name="gt1_grid_check"]')
                           .prop('checked', true);
   }
}
```
スクリプトは登録時以外に動作すれば良いので、条件には以下を設定します。

```
imfr_response_type <> "REGISTRATION"
```
なお、登録時は行の追加・削除を繰り返していると列のidがぐちゃぐちゃになってしまいますが、初期表示時は登録データ順に綺麗にidを1から振り直してくれるようです。

## チェックボックスが2つ以上の場合
複数チェックボックスにする場合は以下のように修正します。

- カスタム列の`input type="checkbox">`タグは全て同じ`name`属性を設定し、`value`属性を個別に設定する。
- 隠しパラメータ列はチェックボックス1つの場合と同じ。
- スクリプトアイテムの`change`イベントで隠し項目列に値を設定する際に、同じ`rowid`でチェックされた項目の`value`値をカンマ区切りにして登録する。
- 初期表示イベントでは、取得した隠し項目列の値を`split(',')`でカンマ分割して、値が`value`値と一致する`<input type="checkbox">`の`checked`プロパティを変更する。

### カスタム列(チェックボックス本体)
「表示したいコードを記述してください。」の部分に、必要な数だけチェックボックスを設定します。

```html
<input type="checkbox" name="gt1_grid_check" value="1">
<input type="checkbox" name="gt1_grid_check" value="2">
<input type="checkbox" name="gt1_grid_check" value="3">
```

### 隠し項目列(データベース接続用)
チェックボックス1つの場合と同じです。

### スクリプトアイテム
スクリプトアイテムを画面上に配置するか、アクション設定-初期表示イベントでカスタムスクリプトを追加し、以下のスクリプトを貼ります。

```js
// カスタム列のチェックボックスにchangeイベントを設定
var tableid = 'gt1'
$('table#' + tableid).on('change', 'input[name="gt1_grid_check"]', function() {
   // グリッドテーブルのIDを取得
   var tableid = $(this).closest('table')
                        .attr('id');
   // クリックされた行IDを取得
   var rowid = $(this).closest('tr')
                      .attr('id');

   // IM-BISクライアントサイドスクリプトAPI
   var args = {};
   args.data = {};
   args.data[tableid] = [];
   args.data[tableid][0] = {};

   // クリックされた行のみ値を設定
   var option = {};
   option.indexKey = rowid;

   // チェックされた値を格納する配列
   var checkboxVal = [];
   // クリックされた行のチェックボックス要素全てに対して処理を実行
   $('table#' + tableid).find('tr#' + rowid)
                        .find('input[name="gt1_grid_check"]')
                        .each(function(index, element) {
      if ($(element).prop('checked')) {
         checkboxVal.push($(element).val());
      }
   })
   // 配列をカンマ区切りで結合
   args.data[tableid][0]['gt1_hidden1'] = checkboxVal.join(',');
   // グリッドテーブルに値を設定
   formaItems.product_80_gridtable.setItemData[tableid](args, option);

   // チェックボックスをオンにする
   for (var i = 0; i < checkboxVal.length; i++) {
      // checkboxVal配列に格納された値と一致するチェックボックスをオンにする
      $('table#' + tableid).find('tr#' + rowid)
                           .find('input[name="gt1_grid_check"][value="' + checkboxVal[i] + '"]')
                           .prop('checked', true);
   }
});
```

チェックボックス1つの時は、値として`value`値をセットするかしないかだけでしたが、2つ以上ある場合はクリックされた行のチェックボックスを`each.function(){}`で全て処理し、チェックがされている項目の`value`値だけを配列`checkboxVal`に格納しています。
その後、配列内の要素を`join(',')`でカンマ結合して隠しパラメータに渡しています。
また、最後のチェックボックスをオンにする処理も、`checkboxVal`に値が存在する項目のみチェックを入れるように修正しています。

### 初期表示イベント
アクション種別はカスタムスクリプトで、以下のコードを貼ります。

```js
// IM-BISクライアントサイドスクリプトAPI
var tableid = 'gt1'
var rowId = '';
var inputIdList = ['gt1_hidden1'];
// グリッドテーブルから値を取得
var result = formaItems.product_80_gridtable.getItemData[tableid](rowId, inputIdList);
var rowid;
var checkboxVal;
for (var i = 0; i < result.length; i++) {
   rowid = i + 1;
   checkboxVal = [];
   // 隠し項目の値をカンマで分割
   checkboxVal = result[i].gt1_hidden1.split(',');
   for (var j = 0; j < checkboxVal.length; j++) {
      // 取得した値からチェックボックスをオンにする
      $('table#' + tableid).find('tr#' + rowid)
                           .find('input[name="gt1_grid_check"][value="' + checkboxVal[j] + '"]')
                           .prop('checked', true);
   }
}
```

チェックボックス1つの時との違いは、取得した値を`split(',')`でカンマ分割し、1つ1つの値と`value`値が一致するチェックのみオンにしているところです。

同様にスクリプトは登録時以外に動作すれば良いので、条件には以下を設定します。

```
imfr_response_type <> "REGISTRATION"
```