テーブルの削除ボタンを押したら確認メッセージを出したい

やりたい事
テーブルの削除ボタンを誤って押してしまい削除されないように
確認メッセージを出したいと思っています。

  1. 'app.record.edit.show’と’app.record.create.show’イベントでテーブルの値を取得し保存します。
  2. テーブルの値が変更されたとき(行の追加、削除、変更)に再度テーブルの値を取得します。
  3. 新規レコード追加時と編集時の両方で動作します。
  4. テーブルの行が削除されようとしたときに確認メッセージを表示し、OKなら削除、キャンセルならテーブルの情報を削除ボタンを押す前に戻します。

発生しているエラー
既存レコードを編集する際は
行を削除しますか⇒OK で正しく行削除が実行されます
行を削除しますか⇒キャンセル で行削除を行う前に戻ります。
新規レコードを追加する際は
行を削除しますか⇒OK で正しく行削除が実行されます。
行を削除しますか⇒キャンセル でエラーが出ます。
エラー内容としては
カスタマイズ用のJavaScriptの実行時にエラーが発生しました。アプリの管理者にお問い合わせください。

  • event.record[‘チェックリスト’].value[0][‘チェック項目’].value が不正です。



    とテーブルの項目がすべて列挙されています。

どうしたらよいのか分からずご教授いただければ幸いです。

実行したコードをコピー&ペーストしましょう

(function() {
  'use strict';
  let previousTableData = null;
  const TABLE_FIELD_CODE = 'テーブルのフィールドコード'; // テーブルのフィールドコードを指定

  function saveTableData(event) {
    console.log('テーブルデータを保存します');
    previousTableData = JSON.parse(JSON.stringify(event.record[TABLE_FIELD_CODE].value));
  }

  function handleTableChange(event) {
    console.log('テーブルが変更されました');
    if (event.changes.row === null) {
      // 行が削除された場合
      if (confirm('本当に行を削除しますか?')) {
        console.log('削除が確認されました');
        saveTableData(event);
      } else {
        console.log('削除がキャンセルされました');
        event.record[TABLE_FIELD_CODE].value = previousTableData;
      }
    } else {
      // 行が追加または変更された場合
      saveTableData(event);
    }
    return event;
  }

  // レコード表示時のイベント
  kintone.events.on(['app.record.edit.show', 'app.record.create.show'], function(event) {
    console.log('レコード表示イベントが発火しました');
    saveTableData(event);
    return event;
  });

  // テーブル変更時のイベント
  kintone.events.on(['app.record.edit.change.' + TABLE_FIELD_CODE, 'app.record.create.change.' + TABLE_FIELD_CODE], handleTableChange);

})();

レコード追加画面でエラーとなるのは、テーブル内フィールドが最初の状態だとundefinedになっており、削除キャンセル時にundefinedが混じった値がeventに含まれてしまい、不正な値と見做されるのではないかと思います。
こちらを参考に、undefinedをフィールド毎に正しい初期値に置き換えてあげる処理を入れてみては如何でしょうか。

たねまき様
返信ありがとうございます!フィールド形式を参考にしたのですが
Uncaught TypeError: Cannot read properties of null (reading ‘hasOwnProperty’)と出ておりどこを修正すればいいか分からない状態になっております・・・

(function() {
  'use strict';
  let previousTableData = null;
  const TABLE_FIELD_CODE = 'テーブルのフィールドコード'; // テーブルのフィールドコードを指定

  function sanitizeTableData(tableData) {
    if (!tableData || !Array.isArray(tableData)) {
      console.error('Invalid table data:', tableData);
      return [];
    }
    return tableData.map(row => {
      if (typeof row !== 'object' || row === null) {
        console.error('Invalid row data:', row);
        return {};
      }
      Object.keys(row).forEach(fieldCode => {
        if (typeof row[fieldCode] === 'string') {
          row[fieldCode] = { value: row[fieldCode] };
        } else if (row[fieldCode] && row[fieldCode].value === undefined) {
          switch (row[fieldCode].type) {
            case 'SINGLE_LINE_TEXT':
            case 'MULTI_LINE_TEXT':
            case 'RICH_TEXT':
              row[fieldCode].value = '';
              break;
            case 'NUMBER':
            case 'DROP_DOWN':
            case 'DATE':
            case 'TIME':
            case 'DATETIME':
              row[fieldCode].value = null;
              break;
            case 'CHECK_BOX':
            case 'USER_SELECT':
            case 'ORGANIZATION_SELECT':
            case 'GROUP_SELECT':
            case 'FILE':
              row[fieldCode].value = [];
              break;
            default:
              row[fieldCode].value = null;
          }
        }
      });
      return row;
    });
  }

  function saveTableData(event) {
    console.log('テーブルデータを保存します', event.record[TABLE_FIELD_CODE].value);
    previousTableData = JSON.parse(JSON.stringify(event.record[TABLE_FIELD_CODE].value));
  }

  function handleTableChange(event) {
    console.log('テーブルが変更されました', event.changes);
    if (event.changes && event.changes.row === null) {
      // 行が削除された場合
      if (confirm('本当に行を削除しますか?')) {
        console.log('削除が確認されました');
        saveTableData(event);
      } else {
        console.log('削除がキャンセルされました。データを復元します', previousTableData);
        try {
          event.record[TABLE_FIELD_CODE].value = sanitizeTableData(previousTableData);
        } catch (error) {
          console.error('テーブルデータの復元中にエラーが発生しました', error);
          // エラーメッセージをユーザーに表示するなどの処理を追加
        }
      }
    } else {
      // 行が追加または変更された場合
      saveTableData(event);
    }
    return event;
  }

  // レコード表示時のイベント
  kintone.events.on(['app.record.edit.show', 'app.record.create.show'], function(event) {
    console.log('レコード表示イベントが発火しました');
    saveTableData(event);
    return event;
  });

  // テーブル変更時のイベント
  kintone.events.on(['app.record.edit.change.' + TABLE_FIELD_CODE, 'app.record.create.change.' + TABLE_FIELD_CODE], handleTableChange);

})();

どこの行でそのエラーが出ているかにもよりますが、hasOwnPropertyとのことなので、

if (typeof row !== 'object' || row === null) {

ここを

if (row === null || typeof row !== 'object') {

こう入れ替えてみるのはどうでしょうか?

たねまき様
ご指示頂いた箇所を修正したところ設定しているタブのプラグインが全く効かなくなってしまいました。
そこで再度修正したのですがhasOwnPropertyのエラーは出なくなったもののconsoleを確認すると
テーブルの1行目は正しく配列に保存されていますが2行目は保存されていないようで、再度最初と同じ状況になっています。
発生しているエラー
既存レコードを編集する際は
行を削除しますか⇒OK で正しく行削除が実行されます
行を削除しますか⇒キャンセル で行削除を行う前に戻ります。
新規レコードを追加する際は
行を削除しますか⇒OK で正しく行削除が実行されます。
行を削除しますか⇒キャンセル でエラーが出ます。
エラー内容としては
カスタマイズ用のJavaScriptの実行時にエラーが発生しました。アプリの管理者にお問い合わせください。

  • event.record[‘チェックリスト’].value[0][‘チェック項目’].value が不正です。



    とテーブルの項目がすべて列挙されています。

そもそもテーブルの値を保存ボタンをクリックする前に配列に入れること可能なのでしょうか?
値が変更されたら・・・としていますが2行目以降はこのトリガーでは動かないのでしょうか?

(function() {
  'use strict';
  let previousTableData = null;
  const TABLE_FIELD_CODE = 'チェックリスト'; // テーブルのフィールドコードを指定
  let tableData = []; // テーブルのデータを格納する配列

  // 新規レコード作成時のテーブル変更イベント
  kintone.events.on('app.record.create.change.' + TABLE_FIELD_CODE, function(event) {
    // テーブルの値を取得
    tableData = event.record[TABLE_FIELD_CODE].value;

    // 取得したテーブルデータを出力(デバッグ用)
    console.log('テーブルデータ:', tableData); 

    return event;
  });

  // 保存前の処理 (必要があれば)
  kintone.events.on('app.record.create.submit', function(event) {
    // 保存前にtableDataを利用する処理をここに記述
    return event;
  });

  function initializeTableData(tableData) {
    if (!tableData || !Array.isArray(tableData)) {
      console.error('Invalid table data:', tableData);
      return [];
    }
    return tableData.map(row => {
      if (typeof row !== 'object' || row === null) {
        console.error('Invalid row data:', row);
        return {};
      }
      Object.keys(row).forEach(fieldCode => {
        if (row[fieldCode] === undefined) {
          switch (row[fieldCode].type) {
            case 'SINGLE_LINE_TEXT':
            case 'MULTI_LINE_TEXT':
            case 'RICH_TEXT':
              row[fieldCode].value = '';
              break;
            case 'NUMBER':
            case 'DROP_DOWN':
            case 'DATE':
            case 'TIME':
            case 'DATETIME':
              row[fieldCode].value = null;
              break;
            case 'CHECK_BOX':
            case 'USER_SELECT':
            case 'ORGANIZATION_SELECT':
            case 'GROUP_SELECT':
            case 'FILE':
              row[fieldCode].value = [];
              break;
            default:
              row[fieldCode].value = null;
          }
        }
      });
      return row;
    });
  }

  function saveTableData(event) {
    console.log('テーブルデータを保存します');
    // テーブルの値を取得 (計算フィールドの値を含む)
    previousTableData = JSON.parse(JSON.stringify(event.record[TABLE_FIELD_CODE].value));
  }

  function handleTableChange(event) {
    console.log('テーブルが変更されました');

    // event.record['チェックリスト'].value が存在し、かつ空配列でないことを確認
    if (event.record[TABLE_FIELD_CODE] && 
        Array.isArray(event.record[TABLE_FIELD_CODE].value) && 
        event.record[TABLE_FIELD_CODE].value.length > 0) {

      if (event.changes && event.changes.row === null) {
        // 行が削除された場合
        if (confirm('本当に行を削除しますか?')) {
          console.log('削除が確認されました');
          saveTableData(event);
        } else {
          console.log('削除がキャンセルされました');
          event.record[TABLE_FIELD_CODE].value = previousTableData || [];
        }
      } else {
        // 行が追加または変更された場合
        saveTableData(event);

        // tableData をコンソールに出力
        console.log('tableData:', tableData); 
      }
    }
    return event;
  }

  // レコード表示時のイベント
  kintone.events.on(['app.record.edit.show', 'app.record.create.show'], function(event) {
    console.log('レコード表示イベントが発火しました');
    saveTableData(event);
    return event;
  });

  // テーブル変更時のイベント
  kintone.events.on(['app.record.edit.change.' + TABLE_FIELD_CODE, 'app.record.create.change.' + TABLE_FIELD_CODE], handleTableChange);
})();

地道にエラーを潰していくしかないですね。
コードをざっと見た感じのエラーから推測される修正箇所候補を書いてみますね。

  • nullやundefinedと比較する箇所は論理否定を使って両方拾えるようにしてみる(参考サイト
  • switch文がフィールド形式を網羅できているか確認する

実際のテーブル内フィールドの構成や、エラーになる際に実際に渡しているデータ(console.log出力結果)等教えていただければ解決に近づくかもしれません。

このようにテーブル内のフィールドコードと、そのフィールドの値が
未定義のときに設定するvalueを紐づければ「value が不正です」の
エラーを回避できるみたいです。

それとpreviousTableDataの値の更新をテーブルの行が増減したときしか
やってなかったので、テーブル内のフィールドの値が変わったときにも
更新するようにしました。ただテーブル内にchangeイベントで指定可能な
フィールド
以外が含まれていると、この更新は完璧にはできませんが。

(() => {
  'use strict';
  
  // テーブルデータを格納する変数
  let previousTableData = null;

  // テーブルのフィールドコード
  const TABLE_FIELD_CODE = 'テーブル';

  // テーブルデータを保存する関数
  const saveTableData = (event) => {
      // テーブルデータを取得
      const tableData = JSON.parse(JSON.stringify(event.record[TABLE_FIELD_CODE].value));
      
      // 各行について処理
      tableData.forEach(row => {
          // 日付が未定義の場合、nullを設定
          if (!row.value['日付'] || !row.value['日付'].value) {
              row.value['日付'] = { type: "DATE", value: null };
          }
          // ドロップダウンが未定義の場合、空文字を設定
          if (!row.value['ドロップダウン'] || !row.value['ドロップダウン'].value) {
              row.value['ドロップダウン'] = { type: "DROP_DOWN", value: "" };
          }
          // 文字列1行が未定義の場合、空文字を設定
          if (!row.value['文字列1行'] || !row.value['文字列1行'].value) {
              row.value['文字列1行'] = { type: "SINGLE_LINE_TEXT", value: "" };
          }
      });

      // テーブルデータを保存
      previousTableData = tableData;
  };

  // テーブルの行増減時の処理をする関数
  const handleTableChange = (event) => {
      // 行が削除された場合
      if (event.changes.row === null) {
          // 確認ダイアログを表示
          if (confirm('本当に行を削除しますか?')) {
              saveTableData(event);
          } else {
              // 削除前のテーブルデータに戻す
              event.record[TABLE_FIELD_CODE].value = previousTableData;
          }
      } else {
          // 行が追加された場合
          saveTableData(event);
      }
      return event;
  };

  // テーブル内のフィールドの値が変わったときの処理をする関数
  const handleFieldChange = (event) => {
      saveTableData(event);
      return event;
  };

  // レコード編集画面または追加画面が表示されたときのイベント
  kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
      saveTableData(event);
      return event;
  });

  // テーブルの行が増減したときのイベント
  kintone.events.on(
    [
      'app.record.edit.change.' + TABLE_FIELD_CODE,
      'app.record.create.change.' + TABLE_FIELD_CODE
    ],
    handleTableChange
  );

  // テーブル内のフィールドの値が変わったときのイベント
  kintone.events.on([
      'app.record.edit.change.日付',
      'app.record.create.change.日付',
      'app.record.edit.change.ドロップダウン',
      'app.record.create.change.ドロップダウン',
      'app.record.edit.change.文字列1行',
      'app.record.create.change.文字列1行'
  ], handleFieldChange);
})();

たねまき様
ありがとうございます!
いただいたアドバイスを基にエラー原因を探っていこうと思います!

もみじ様
コメントありがとうございます!
アドバイス&コードもありがとうございます。

お陰様でやりたい事が実現できました!!
因みに添付ファイルがテーブル内にあるのですがこれには対応できるのでしょうか?

こちらに記載がある通り、添付ファイルはshowイベント内での書き換えに非対応なので対応できないと思います。

添付ファイルも対応させようとするなら、kintone非推奨のDOM操作を行い、標準の行削除ボタンを非表示にして独自の行削除ボタン(上書きではなく行削除そのものをキャンセルする考え方)を実装する等、少し手間のかかるカスタマイズが必要になるかと思います。

たねまき様
ありがとうございます。
今回のテーブルの仕様変更でボタンのDOM操作が変わってしまったので
できればやりたくなかったのですが
添付ファイルがある以上どうしようもなさそうですね。
添付ファイルが無い場合は教えて頂いたコードを利用しようと思います!

沢山教えて頂きありがとうございました!

このトピックはベストアンサーに選ばれた返信から 3 日が経過したので自動的にクローズされました。新たに返信することはできません。