kintoneアプリストアのSE工数管理日報を使ってサブテーブルの表示・非表示を実装してみた

kintoneでは、DOMの操作やkintoneのCSSを上書きすることは、禁じ手となっているようですが、
自己責任でやる分には全然問題無いと個人的には思っています。
おすすめしません！と言っていても、上級者のみなさんは、JavaScriptやCSSでkintoneのDOMを操作しているに違いないと心の中で思っています。

という訳で、禁断のJavaScriptとCSSでkintoneのDOMを操作してみるサンプルコードを載せてみました。

#フィールド編集画面
サンプルには、kintoneアプリストアにある「SE工数管理日報」を使い、少しフィールドを追加したりしています。
<img width="1226" alt="スクリーンショット 2018-10-17 5.51.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/214523/9ed5a6cc-7f1f-ff87-dd59-aa072d29f1bd.png">
追加したフィールド（フィールドコード）

- 時刻表示（時刻表示）　ラジオボタン
- サブテーブル（Table）
    - 作業内容詳細（作業内容詳細）　文字列複数
- スペース（Table2）

#詳細画面

1. ラジオボタンの時刻表示の表示を選んで、開始時刻と終了時刻を表示した場合
![screencapture-od4sa-cybozu-k-186-show-2018-10-17-06_09_07.png](https://qiita-image-store.s3.amazonaws.com/0/214523/6c67cc6f-d93d-873a-b707-6e87a8f4174a.png)

2. 非表示を選んで、時刻を非表示とした場合
![screencapture-od4sa-cybozu-k-186-show-2018-10-17-06_09_28.png](https://qiita-image-store.s3.amazonaws.com/0/214523/b7e9adcb-1ca2-e07e-bb72-c7140f170e30.png)

#編集画面
1. 時刻表示
![screencapture-od4sa-cybozu-k-186-show-2018-10-17-06_05_02.png](https://qiita-image-store.s3.amazonaws.com/0/214523/a7d4b1b4-759c-9543-a8e8-a3fc409db384.png)
2. 時刻非表示
![screencapture-od4sa-cybozu-k-186-show-2018-10-17-06_06_17.png](https://qiita-image-store.s3.amazonaws.com/0/214523/e9344b41-2b22-22bc-b56b-7dc75df2742f.png)

# サンプルコード

JavaScriptファイル

```javascript
(function() {
    'use strict';

    function split2nl(str) {
        console.log('文字列を改行コードで分割して配列で返す');
        console.log(str);
        let ary;
        if (/\r\n/g.test(str)) {
            console.log(str.match(/\r\n/g));
            ary = str.split(/\r\n/g);
            console.log(ary);
        } else if (/\n|\r/g) {
            console.log(str.match(/\n|\r/g));
            ary = str.split(/\n|\r/g);
            console.log(ary);
        }
        return ary;
    }

    function text2Div(text) {
        let elDiv = document.createElement('div');
        elDiv.className = 'kuc-label';
        let elText1 = document.createTextNode(text);
        elDiv.appendChild(elText1);
        return elDiv;
    }

    // 時刻非表示テーブルの生成
    function setTable2(detailArray) {
        const text = new kintoneUIComponent.Text({value: 'input text'});
        text.disable();
        const text2 = new kintoneUIComponent.Text({value: 'input text'});
        text2.disable();
        const text3 = new kintoneUIComponent.Text({value: 'input text'});
        text3.disable();
        const text4 = new kintoneUIComponent.Text({value: 'input text'});
        text4.disable();

        let table = new kintoneUIComponent.Table({
            rowTemplate: [text, text2, text3, text4],
            header: ['作業時間', '作業内容','作業内容詳細','備考'],
        });
        // テーブルにデータをセット
        table.setValue(detailArray);

        // テーブルの'作業内容詳細'セルの要素を入れ替える        
        let domTable = table.render(); // DOM
        let domTbody = domTable.getElementsByClassName('kuc-table-tbody');
        let domTd = domTable.getElementsByClassName('kuc-table-td');

        for (let i = 0, row = detailArray.length; i < row; i++) {
            let n = 5 * i + 2;
            let elKinputOuter = domTd[n].getElementsByClassName('kuc-input-outer');
            let tPlace = detailArray[i][2];
            let aryText = split2nl(tPlace);
            // 子ノード削除
            elKinputOuter[0].removeChild(elKinputOuter[0].lastChild);
            for (let i = 0; i < aryText.length; i++) {
                // 子ノード追加
                elKinputOuter[0].appendChild(text2Div(aryText[i]));
            }

        }

        // テーブル表示
        console.log('時刻非表示テーブルの生成');
        kintone.app.record.getSpaceElement('Table2').appendChild(domTable);

    }

    // kintone レコードを時刻非表示テーブルに流し込む用の配列に生成
    function fetchRecords2Array(kintoneRecord) {

        let record = kintoneRecord;
        let detailArray = [];
        for( var i = 0; i < record.Table.value.length; i++) {
            let detailRow = [];
            detailRow.push(record.Table.value[i].value['作業時間'].value);
            detailRow.push(record.Table.value[i].value['作業内容'].value);
            detailRow.push(record.Table.value[i].value['作業内容詳細'].value);
            detailRow.push(record.Table.value[i].value['備考'].value);
            detailArray.push(detailRow);
        }
        console.log(detailArray);
        return detailArray;
        
    }

    const events = ['app.record.detail.show',
                    'app.record.create.show',
                    'app.record.create.change.時刻表示',
                    'app.record.edit.show',
                    'app.record.edit.change.時刻表示'];
    // 詳細画面表示
    kintone.events.on(events, function(event) {
        console.log(event.type);
        setFieldOff(event);
    });

    function setFieldOff(kintoneEvent) {
        console.log('明細用テーブルを一旦非表示にする');
        kintone.app.record.setFieldShown('Table', false);
        kintone.app.record.setFieldShown('Table2', false);
        let elTable2 = kintone.app.record.getSpaceElement('Table2');

        let record = kintoneEvent.record;
        let detailArray = fetchRecords2Array(record);
        
        const radioBtn = record['時刻表示'].value;
        switch(radioBtn) {
            case '表示':
                console.log('表示切替：時刻表示');
                kintone.app.record.setFieldShown('Table', true);
                console.log(document.getElementById('user-js-Table2'));
                document.getElementById('user-js-Table2').style.display = 'none';
                break;
            case '非表示':
                console.log('表示切替：時刻非表示');
                // 印刷用テーブル表示
                let elTable2 = kintone.app.record.getSpaceElement('Table2');
                console.log(elTable2);
                console.log(elTable2.parentNode);
                console.log(elTable2.firstElementChild);
                console.log(document.getElementById('user-js-Table2'));
                if (elTable2.firstElementChild === null) {
                    setTable2(detailArray);
                }
                if (document.getElementById('user-js-Table2').style.display === 'none') {
                    document.getElementById('user-js-Table2').removeAttribute('style');
                }
                console.log(elTable2.firstElementChild);
                console.log(document.getElementById('user-js-Table2'));
                break;
            default:
                console.log('表示切替：何も選択されていません');
        }
    }

})();

```

CSSファイル

```css
#user-js-Table2 .kuc-input-text {
  height: 18px;
  border: none;
  background-color: #f5f5f5;
}

#user-js-Table2 .kuc-input-outer {
  background-color: #f5f5f5;
}

#user-js-Table2 .kuc-icon-btn {
  visibility: hidden;
}

#user-js-Table2 .kuc-table-td {
  padding: 1px;
}

.kuc-label {
  padding: 0px 12px 0px 12px;
  color: #000000;
}

```

#説明
何をやっているかはコードをみてもらうとして、何をやりたかったかについて（何に使えるか）少し書きます。

- [kintone UI Component](https://developer.cybozu.io/hc/ja/articles/360000511023)を使ったサブテーブル操作の方法
- サブテーブル内のフィールドの表示・非表示の切り替え方法の提案
- 今回実装していませんが、印刷時に表示したくないフィールドの切り替えに利用する

などなど。

kintoneライクなUIを作成できる [kintone UI Component](https://developer.cybozu.io/hc/ja/articles/360000511023) は基本的に入力フォーム用に出来ているので、今回それを表示用に操作してなんとかそれらしいことを実現してみました。

お約束ですが、kintoneのDOM操作は推奨されていませんので、くれぐれも自己責任でお願いします。

でもDOM操作していることが分かっていれば、ある日kintoneのDOMが変わっても直せますよね？