【フォームブリッジ】テーブルフィールドにExcelファイルで入力する機能をつける【 #トヨクモkintoneフェス 】

当サイトではアフィリエイト広告を利用して商品を紹介しています。

こんにちはそういちろうです。今回はフォームブリッジのカスタマイズの紹介です。
JavascriptとCSSのカスタマイズを利用して標準機能ではできないフォームブリッジのテーブルフィールドへの入力をExcel取り込みをできるようにカスタマイズを紹介します。
カスタマイズにあたっては生成AIのサポートを受けながらスクリプトを作成しました。
9割は自分で理解しているコードのみを使用していますが、使用していての不具合だったり、今後のアップデートで使用できなくなったりする可能性はあります。その点ご了承ください

今回できるようになること

テンプレートのExcelファイルを用意してそこに値を入力します。
Excelファイルをフォームブリッジ上で選択することでExcelデータを読み取りテーブルに値を反映させます。

インポートしてフォームブリッジを作成

上記のファイルをダウンロードします。
フォームブリッジの新しいフォームを作るからインポートして作成を選びフォームを作成してください。

使い方

作成したフォームに以下のサンプルのExcelを選択して読み込ませてください。
画像のようにデータが読み込まれたら成功です。

実際のフォーム※お試し環境なので7月1日ごろに使えなくなります。

コードの解説


2つのJavaScriptファイルからこの機能を作成しています。

SheetJS

スプレッドシートを読み込みや編集エクスポートまでサポートするものです。
下記のURLを設定することで、簡単にExcelの操作が可能になります。

公式サイト

https://cdn.sheetjs.com/xlsx-0.20.2/package/dist/xlsx.full.min.js

excelImport.js

今回作成したファイルです。中身は以下のようになっています。
テーブルのフィールドコードやドロップエリアのテキストラベルのテキストを変更する場合にはconfigオブジェクトの中身を変更してください。

(function () {
    "use strict";

    // 設定オブジェクト
    const config = {
        tableCode: "テーブル", // テーブルのフィールドコード
        dropAreaText: 'ファイルをここにドラッグ&ドロップしてください', // ドロップエリアのテキスト
        excelLabelText: 'Excel読み込み', // 検索するラベルテキスト
        dropAreaId: 'dropArea', 
        inputId: 'excelInput', 
        dropAreaStyles: {
            border: '2px dashed #ccc',
            padding: '20px',
            textAlign: 'center',
            cursor: 'pointer',
            transition: 'background-color 0.3s ease'
        },
    };

    /**
     * フォームがマウントされた時に実行される関数
     * @param {object} state - フォームの状態オブジェクト
     * @returns {object} - 更新された状態オブジェクト
     */
    fb.events.form.mounted = [function (state) {
        // 指定されたラベルテキストを持つ要素を取得
        const labels = document.querySelectorAll('.column.el-form-item label p');
        let targetField = null;

        labels.forEach(label => {
            if (label.innerText.includes(config.excelLabelText)) {
                targetField = label.parentElement;
            }
        });

        if (!targetField) {
            console.error(`${config.excelLabelText}ラベルが見つかりません。`);
            return state;
        }

        // ドラッグアンドドロップ用の領域を作成
        const dropArea = document.createElement('div');
        dropArea.id = config.dropAreaId;
        Object.assign(dropArea.style, config.dropAreaStyles);
        dropArea.innerHTML = config.dropAreaText;

        // ファイル入力フィールドを作成
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = '.xlsx, .xls';
        input.id = config.inputId;
        input.style.display = 'none';

        // ファイル読込結果の出力エリアを作成
        const output = document.createElement('div');
        output.id = 'output';
        output.style.marginTop = '10px';
        output.style.color = '#333';

        // ドラッグアンドドロップ領域にファイル入力フィールドと出力エリアを追加
        dropArea.appendChild(input);
        targetField.appendChild(dropArea);
        targetField.appendChild(output);

        // ドラッグオーバーとドラッグリーブイベントの設定
        dropArea.addEventListener('dragover', function (event) {
            event.preventDefault();
            dropArea.style.backgroundColor = '#f0f0f0';
        }, false);

        dropArea.addEventListener('dragleave', function (event) {
            event.preventDefault();
            dropArea.style.backgroundColor = '#fff';
        }, false);

        // ドロップイベントの設定
        dropArea.addEventListener('drop', function (event) {
            event.preventDefault();
            dropArea.style.backgroundColor = '#fff';
            const files = event.dataTransfer.files;
            handleFile(files);
        }, false);

        // ファイルが選択されたときに処理する関数
        input.addEventListener('change', function (event) {
            const files = event.target.files;
            handleFile(files);
        }, false);

        /**
         * ファイルを処理する関数
         * @param {FileList} files - ファイルリスト
         */
        function handleFile(files) {
            const file = files[0];
            if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.xls')) {
                output.textContent = 'エラー: 有効なExcelファイルを選択してください。';
                output.style.color = 'red';
                return;
            }
            const reader = new FileReader();
            reader.onload = function (e) {
                const data = e.target.result;
                const workbook = XLSX.read(data, { type: 'binary' });
                const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
                const excelData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });

                // ヘッダー行を取得
                const headers = excelData[0];
                // データ行を取得
                const dataRows = excelData.slice(1);

                // テーブルにデータを設定
                dataRows.forEach((row, rowIndex) => {
                    headers.forEach((fieldCode, colIndex) => {
                        const value = row[colIndex];
                        if (state.record[config.tableCode] && state.record[config.tableCode].value[rowIndex]) {
                            state.record[config.tableCode].value[rowIndex].value[fieldCode].value = value;
                        } else {
                            // 新しい行を追加する場合
                            const newRow = {};
                            headers.forEach((code) => {
                                newRow[code] = { value: '' };
                            });
                            state.record[config.tableCode].value.push({ value: newRow });
                            state.record[config.tableCode].value[rowIndex].value[fieldCode].value = value;
                        }
                    });
                });

                // DOMに反映させる
                state.record[config.tableCode].value.forEach((row, rowIndex) => {
                    headers.forEach((fieldCode) => {
                        const tableElement = fb.getTableElementsByCode(config.tableCode, fieldCode)[rowIndex];
                        if (tableElement) {
                            tableElement.getElementsByTagName('input')[0].value = row.value[fieldCode].value;
                        }
                    });
                });

                // 読み込んだ結果を出力
                output.textContent = `ファイル "${file.name}" が正常に読み込まれました。`;
                output.style.color = 'green';

                console.log(state.record[config.tableCode].value);
            };
            reader.readAsBinaryString(file);
        }

        return state;
    }];
})();

おわりに

今回はフォームブリッジのテーブルにExcelで値を入力するというテーマで記事を書いてみました。
実務でこれが必要になったケースは、フォームブリッジで入力したレコードをコピーして少し変更して再申請したいというケースでした。
kintone上でのExcelへの変換は様々なプラグインが用意されていてそちらについてはCustomineで実装しました。
そのファイルをkビューワー上に表示して、コピーして入力したい場合には、そのレコードからExcelをダウンロードして、フォームブリッジ上でそのままExcelファイルを読み込むことで擬似的にレコードのコピーを再現しています。

この記事が誰かの役に立てたら幸いです。

トヨクモkintoneフェス2024のカウントダウンカレンダーは7月2日までつづきます。
このあともお楽しみに!

それではありがとうございました。

kintone

Posted by sochan