4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

kintoneAdvent Calendar 2023

Day 25

Promiseでサブテーブルの特定行を非表示にする

Last updated at Posted at 2023-12-25

はじめに

当方、kintoneを最近利用し始めまたばかりの初心者です。kintoneは、ノーコードで色々な業務ツールをクラウド上で作れて非常に優れモノだと感心していましたが、一般機能では実現できないことをJavaScriptでカスタマイズできることを知り、更に興味を持ち始めています。(JavaScriptを碌に知りもせずなのですが:sweat_smile:

今回やりたかったことは、あるアプリ内に配置したサブテーブルに非表示チェックボックス列を置き、これをチェックすることでその行を非表示にしたいということです。

以下のcybozu developer Communityのご解答を参考にさせていただき、解決に至るまでに得た知識を簡単にまとめてみました。

解答反映前のコード

上記解答を反映させる前の動作しないコードは以下のとおりです。
今回対象のコントロールのフィールドコードは、サブテーブルがdocumentInfo、非表示チェックボックスがisHiddenとなります。デバッグ用のconsole.logはそのままにしています。

(function() {
  'use strict';
  
  kintone.events.on('app.record.detail.show', function(event) {
    let record = event.record;
    let tableData = record.documentInfo.value;
    
    let dTable = kintone.app.record.getFieldElement('documentInfo');
    let dtBody = dTable.tBodies[0];
    let dtRows = dtBody.rows;    
    console.log('tableData.length=' + tableData.length);
    for (let i = 0; i < tableData.length; i++) {
      console.log(i + '回目');
      if (tableData[i].value.isHidden && tableData[i].value.isHidden.value[0]) {
        if (dtRows) {
          console.log('dtRows[' + i + '] はイケてる');
          dtRows[i].style.display = 'none'
        } else {
          console.log('dtRows[' + i + '] がundefined');
        }
      }
    }
    return event;
  });

})();

以下でstyleundefinedになってしまいnoneを設定できません。

dtRows[i].style.display = 'none'

行き詰っていたところ上記解答を見つけましたが、これを反映させるには、そこに登場するPromiseの理解が必要でした。

Promiseとは?

以下が解答のコードです。JavaScript初心者の自分には、最初は何をしているのかさっぱりわかりませんでした。


const trDrawWait = (table, tableClass) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        let recordData = kintone.app.record.get(), rec = recordData.record;

        if ((rec[table].value.length + 1) === Array.from(document.getElementsByClassName(tableClass))[0].querySelectorAll('tr').length) {
          resolve();
        } else {
          resolve(trDrawWait(table, tableClass));
        }
      } catch(error) {
        resolve();
      }
    }, 500);
  });
};

Promiseとは、JavaScriptの組み込みオブジェクトで、コンストラクタ関数(クラスのようなもの)です。コンストラクタ関数とは、関数の構文で定義し、生成されたインスタンスがクラスのように振る舞うものです。
Promiseが何をしているのか、以下にPromiseの生成を単純化したコードを示します。

let hoge = new Promise(function(resolve, reject) {
  // 通常は非同期処理

 // 成功時
  resolve();
  // 必要なら失敗時
  // reject();
});

hoge.then(function(value) {
    // 処理成功時
}).catch(function(error) {
    // 処理失敗時
});
  • newPromiseオブジェクトを生成。
  • Promiseの引数として渡す関数のことをexecutorと言う。
  • executorは、通常は非同期関数を含んだものを渡す。
  • executorは、必ずしも非同期関数を含んでいなくても良い。
  • thenexecutor内でresolveが呼び出されないと実行されない。
  • 同様にcatchexecutor内でrejectが呼び出されないと実行されない。
  • executorの引数はその順番が重要で、第一引数がresolve、第二引数がrejectと定められている。
  • resolverejectの名前は自由に変えられる。

また、これは今回のケースとは関係ないですが、

  • resolveresolve(data)のように引数を渡すとthenで定義する関数の引数で受け取れる。
  • これはrejectcatchの関係についても同様。
  • thenは複数を連ねることができる。これをPromiseチェーンと言う。
  • catchは一つのみで複数を連ねることはできない。

解答コードについて、

  • setTimeoutという非同期関数により非同期処理が行われる。
  • レコードデータ数とサブテーブルのtr要素数が一致することで解決処理を実行する。
    tr要素のレンダリングの遅延に対応したもの)
  • 行数が不一致となる場合、自分自身を再帰呼出しする。
  • trycatchcatchは、Promisecatchメソッドとは別物。

反映させてみる

実際に自分のアプリにこの解答を反映させた最終コードが以下です。

(function() {
  'use strict';
  
  let count = 1;
  const trDrawWait = (table, tableClass, depth = 0, maxDepth = 1000) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let recordData = kintone.app.record.get(), rec = recordData.record;

          if ((rec[table].value.length + 1) === Array.from(document.getElementsByClassName(tableClass))[0].querySelectorAll('tr').length) {
            console.log(count);
            resolve();
          } else if (depth >= maxDepth) {
            console.log("最大ネスト数に達しました。")
            resolve();  
          } else {
            console.log(count++);
            resolve(trDrawWait(table, tableClass, depth + 1, maxDepth));
          }
        } catch(error) {
          resolve();
        }
      }, 500);
    });
  };

  kintone.events.on('app.record.detail.show', function(event) {
    let record = event.record;
    let tableData = record.documentInfo.value;
    
    // 2つ目の引数はサブテーブルのクラス名。ブラウザのデバッグ機能で確認
    trDrawWait('documentInfo', 'subtable-gaia subtable-5520749 show-subtable-gaia').then(() => {
      let dTable = kintone.app.record.getFieldElement('documentInfo');
      let dtBody = dTable.tBodies[0];
      let dtRows = dtBody.rows;    
      console.log('tableData.length=' + tableData.length);
      for (let i = 0; i < tableData.length; i++) {
        console.log(i + '回目');
        if (tableData[i].value.isHidden && tableData[i].value.isHidden.value[0]) {
          if (dtRows) {
            console.log('dtRows[' + i + '] はイケてる');
            dtRows[i].style.display = 'none'
          } else {
            console.log('dtRows[' + i + '] がundefined');
          }
        }
      }
    });
    
    return event;
  });

})();

若干書き換えていますが、ほぼパクリです。再帰呼出の回数制限を設けてみました。

解答者様並びにcybozu developer Communityに集う皆々様には感謝いたします。お陰様で問題を解決できました。本当にありがとうございます:bow_tone1:
kintoneはノーコードが売りですが、これからも皆様の助けお借りしつつ、カオスなJavaSriptと戯れる場所として、kintoneカスタマイズを利用したいと思っています:laughing:

参考リンク

4
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?