はじめに
当方、kintoneを最近利用し始めまたばかりの初心者です。kintoneは、ノーコードで色々な業務ツールをクラウド上で作れて非常に優れモノだと感心していましたが、一般機能では実現できないことをJavaScriptでカスタマイズできることを知り、更に興味を持ち始めています。(JavaScriptを碌に知りもせずなのですが)
今回やりたかったことは、あるアプリ内に配置したサブテーブルに非表示チェックボックス列を置き、これをチェックすることでその行を非表示にしたいということです。
以下の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;
});
})();
以下でstyle
がundefined
になってしまい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) {
// 処理失敗時
});
-
new
でPromise
オブジェクトを生成。 -
Promise
の引数として渡す関数のことをexecutor
と言う。 -
executor
は、通常は非同期関数を含んだものを渡す。 -
executor
は、必ずしも非同期関数を含んでいなくても良い。 -
then
はexecutor
内でresolve
が呼び出されないと実行されない。 - 同様に
catch
はexecutor
内でreject
が呼び出されないと実行されない。 -
executor
の引数はその順番が重要で、第一引数がresolve
、第二引数がreject
と定められている。 -
resolve
とreject
の名前は自由に変えられる。
また、これは今回のケースとは関係ないですが、
-
resolve
にresolve(data)
のように引数を渡すとthen
で定義する関数の引数で受け取れる。 - これは
reject
~catch
の関係についても同様。 -
then
は複数を連ねることができる。これをPromiseチェーンと言う。 -
catch
は一つのみで複数を連ねることはできない。
解答コードについて、
- setTimeoutという非同期関数により非同期処理が行われる。
- レコードデータ数とサブテーブルの
tr
要素数が一致することで解決処理を実行する。
(tr
要素のレンダリングの遅延に対応したもの) - 行数が不一致となる場合、自分自身を再帰呼出しする。
-
try
~catch
のcatch
は、Promise
のcatch
メソッドとは別物。
反映させてみる
実際に自分のアプリにこの解答を反映させた最終コードが以下です。
(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に集う皆々様には感謝いたします。お陰様で問題を解決できました。本当にありがとうございます
kintoneはノーコードが売りですが、これからも皆様の助けお借りしつつ、カオスなJavaSriptと戯れる場所として、kintoneカスタマイズを利用したいと思っています
参考リンク