サブテーブルの行毎に確認ボタンを設置

お世話になります。
kintoneカスタマイズ、JavaScript 初心者です。よろしくお願いします。

サブテーブルに「確認」というチェックボックスを設定(図1)し、詳細画面で特定の人にのみボタンを設置(図2)しました。
「未確認」ボタンをクリックすると、その行を確認したことになり、ボタンは「確認済」に変わります。
いちいち編集画面に切り替えて操作するのが面倒だという事で、詳細画面から確認済のチェックボックスをオンに出来る様にしています。

明細行にボタンを設置する方法」、「テーブルの値を更新するボタンを作成したいです」、「テーブルの操作」等を参考にしました。

一応、目的の事は達成できたのですが、こんなやり方でいいのか、もっとスマートな方法があるのかを知りたくて投稿させていただきました。
また、似た様なことをされたい方のヒントになれば幸いです。

コードの 37行目で表からチェックボックスの値を取得する部分で、チェックボックスに設定した文字列が 2つ繰り返した文字列が返ってくるのが原因不明ですっきりしません。理由をご存じの方がおられたら教えていただきたいです。

(図1)

(図2)

 

(コード)

//  表形式の行毎に確認するボタンの設置と更新処理
(() => {
"use strict";
const table_order = 0; //1つ目のテーブル
const judge_order = 2; //3列目で判別
const button_order = 2; //3列目にボタンを表示
const checkedString = '確認済'; //チェックボックスに設定した文字列
const chairmanshipId = 1; //確認ボタンが表示される人のユーザID
var appId;
var recordId;
var subRecords = []; //テーブルの行IDを保存用

kintone.events.on('app.record.detail.show', (event) => {
  //アプリID・レコードIDを取得
  appId = event.appId;
  recordId = event.recordId;
  //ログインユーザーIDを取得
  const record = event.record;
  const loginUser = kintone.getLoginUser();
  const loginUserId = loginUser['id'];
  //サブテーブルの行リストを取得
  for(let i = 0; i < record['table1']['value'].length; i++){
    subRecords[i] = record['table1']['value'][i]['id'];
  }

  //特定のユーザの場合のみボタンを表示
  if(loginUserId == chairmanshipId){
    $(() => {
      setTimeout(() => {
        var rows = $('table:eq(' + table_order + ') tbody tr'); //行リストを配列に取得
        var str; //ボタンに表示する文字列
        var bgcolor; //ボタン背景色
        var disabled; //ボタン無効化用文字列
        var line = 0; //行カウンタ
        var button;
        $.each(rows, function(){ //ここは ()=>{ だとエラーになる
          if($(this).children("td").eq(judge_order).find("span").text() == **checkedString + checkedString** ){
            str = '確認済';
            bgcolor = '808080';
            disabled = ' disabled';
          }else{
            str = '未確認';
            bgcolor = 'ff8080';
            disabled = '';
          }
          button = `<button id="check_button_${subRecords[line]}" style="display:block;width:80px;margin:2px auto;background:#${bgcolor};text-align:center;padding:2px;"${disabled}>${str}</button>`;
          $(this).children("td").eq(button_order).html(button); //ボタンを表示
          const buttonObj = document.getElementById(`check_button_${subRecords[line]}`);
          buttonObj.onclick = () => {
//id名から行ID を取り出し
            const buttonId = buttonObj.id.slice(13);
            //更新用データを構築
            var prm = {
              "app": appId,
              "id": recordId,
              "record": {
                "table1": {
                  "value": []
                }
              }
            }
            for(let i = 0; i < subRecords.length; i++){
              // この行が無いと、次の行がエラーになる
              prm['record']['table1']['value'][i] = {};
              // id が無いと、その行は削除されてしまう
              prm['record']['table1']['value'][i]['id'] = subRecords[i];
              if(subRecords[i] == buttonId){
                prm['record']['table1']['value'][i]['value'] = {};
                prm['record']['table1']['value'][i]['value']['checked'] = {};
                // prm['record']['table1']['value'][i]['value']['checked']['type'] = 'CHECK_BOX';
                prm['record']['table1']['value'][i]['value']['checked']['value'] = [checkedString];
              }
            }
//レコードを更新
            var retd = kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', prm);
            //表示を更新
            location.reload();
          }
          line++;
        });
      }, 300);
    });
  }
});
})();

その方法で良いのかどうかですが、確実に良い方法とは言えません。処理がDOM操作になっているので、kintoneがアップデートでデザインを変更した場合、動作しなくなる可能性があります(DOM操作の場合、kintoneが未来永劫処理内容が動くことを保証しているわけではありません)。
無論デザインの変更に合わせて処理内容を変更すれば問題ありませんが、その時にカスタマイズできる方がいるかどうか、という問題になります。

また1つ目のテーブル要素を取得して処理なので、フォームレイアウトでそのサブテーブルより前に他のサブテーブルや関連レコードを設置した場合も動作しなくなるかと思います(こちらは注意さえすれば問題ないかもしれません)。

 

しかし、代替の方法(kintoneが処理内容が動くことを保証している方法)があるかというと、現状はありません。サブテーブルの要素を取得する方法が用意されていないので、サブテーブルの各行に、という処理をする場合は結局のところDOM操作で行うしかありません。

他に気になった部分として、setTimeoutでサブテーブルに対しての処理を遅らせてありますが、サブテーブルはレコードを表示後に各行を描画していく処理になっているので、サブテーブルが数十行になった場合、下の方の行は処理が適用されないかもしれません。これの代替方法は、setTimeoutの時間を伸ばすか、他の方法で対応できます(参考例)。

 

また37行目で「確認済確認済」となってしまう原因ですが、取得するフィールドの中身は「<span><span>確認済</span></span>」となっています。jQueryのfind()は一致した要素全てを取得する動作を行うので、
①1つめのspanを取得し、そのtext()で確認済を取得
②span要素の中のspanを取得し、そのtext()で確認済を取得
③①と②で取得した確認済を連結し「確認済確認済」

という動作になっています。「確認済」と比較させるのであれば

$(this).children("td").eq(judge_order).find("span").find("span").text()

もう1つfind(“span”)を増やせば問題ありません。

mis-hashimoto 様

ありがとうございます。

代替の方法(kintoneが処理内容が動くことを保証している方法)があるかというと、現状はありません。

この部分、少し安心しました。

また37行目で「確認済確認済」となってしまう原因ですが、取得するフィールドの中身は「<span><span>確認済</span></span>」となっています。

ありがとうございます!すごく納得です。
確認したところ、参考にした「明細行にボタンを設置する方法」では、一行テキストだったので、<span> は 1つだけ、私はチェックボックスだったので、<span> が二重になっていたという事ですね。すっきりしました。

mis-hashimoto さんのコメントを参考に、サブテーブルの行数が増えても、すべてボタンが表示されるように、かつ行数が少ない時に必要以上の待ち時間が無いように改良してみました。

22行目: rowCount サブテーブルの行数を変数に入れる
23行目: for文のループ回数を rowCount に変更
30行目: setTimeout から普通の関数 checkDispComplete 定義に変更
32~36: 全行が表示されていなければ、タイマ再セットして抜ける
87行目: setTimeout ではなくなったので、普通の関数の終わりに変更
88行目: checkDispComplete() を最初の呼び出し

//  表形式の行毎に確認するボタンの設置と更新処理
(() => {
  "use strict";
  const table_order = 0;   //1つ目のテーブル
  const judge_order = 2;   //3列目で判別
  const button_order = 2;  //3列目にボタンを表示
  const checkedString = '確認済';  //チェックボックスに設定した文字列
  const chairmanshipId = 1;        //確認ボタンが表示される人のユーザID
  var appId;
  var recordId;
  var subRecords = [];    //テーブルの行IDを保存用

  kintone.events.on('app.record.detail.show', (event) => {
    //アプリID・レコードIDを取得
    appId = event.appId;
    recordId = event.recordId;
    //ログインユーザーIDを取得
    const record = event.record;
    const loginUser = kintone.getLoginUser();
    const loginUserId = loginUser['id'];
    //サブテーブルの行リストを取得
  const rowCount = record['table1']['value'].length; // <-- この行を追加
    for(let i = 0; i < rowCount; i++){
      subRecords[i] = record['table1']['value'][i]['id'];
    }

    //特定のユーザの場合のみボタンを表示
    if(loginUserId == chairmanshipId){
      $(() => {
      const checkDispComplete = function(){ // <-- ここを変更
          var rows = $('table:eq(' + table_order + ') tbody tr');  //行リストを配列に取得
        if(rows.length < rowCount){ // <-- ここの if文を追加
            //まだすべて表示し終わっていないので、100m秒後に再度呼び出す様にして抜ける
          setTimeout(checkDispComplete, 100);
            return;
          }
          var str;       //ボタンに表示する文字列
          var bgcolor;   //ボタン背景色
          var disabled;  //ボタン無効化用文字列
          var line = 0;  //行カウンタ
          var button;
          $.each(rows, function(){     //ここは ()=>{ だとエラーになる
            if($(this).children("td").eq(judge_order).find("span").text() == checkedString + checkedString){
              str = '確認済';
              bgcolor = '808080';
              disabled = ' disabled';
            }else{
              str = '未確認';
              bgcolor = 'ff8080';
              disabled = '';
            }
            button = `<button id="check_button_${subRecords[line]}" style="display:block;width:80px;margin:2px auto;background:#${bgcolor};text-align:center;padding:2px;"${disabled}>${str}</button>`;
            $(this).children("td").eq(button_order).html(button);    //ボタンを表示
            const buttonObj = document.getElementById(`check_button_${subRecords[line]}`);
            buttonObj.onclick = () => {
              //id名から行ID を取り出し
              const buttonId = buttonObj.id.slice(13);
              //更新用データを構築
              var prm = {
                "app": appId,
                "id": recordId,
                "record": {
                  "table1": {
                    "value": []
                  }
                }
              }
              for(let i = 0; i < subRecords.length; i++){
                // この行が無いと、次の行がエラーになる
                prm['record']['table1']['value'][i] = {};
                // id が無いと、その行は削除されてしまう
                prm['record']['table1']['value'][i]['id'] = subRecords[i];
                if(subRecords[i] == buttonId){
                  prm['record']['table1']['value'][i]['value'] = {};
                  prm['record']['table1']['value'][i]['value']['checked'] = {};
                  // prm['record']['table1']['value'][i]['value']['checked']['type'] = 'CHECK_BOX';
                  prm['record']['table1']['value'][i]['value']['checked']['value'] = [checkedString];
                }
              }
              //レコードを更新
              var retd = kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', prm);
              //表示を更新
              location.reload();
            }
            line++;
          });
      }; // <-- 変更
       checkDispComplete(); // <-- 追加
      });
    }
  });
})();

 

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