目次
結論
JavaScriptカスタマイズ=ノーコード
トヨクモさん製品の中でも激推しの「フォームブリッジ」をテーマに、今回は近頃のゲームチェンジャー「生成AI」によって、上記理論となっている?という視点で語ってみたいと思います。
はじめに
今回、トヨクモさんのAdvent Calendar 2024として記事をアップするのですが、皆さまnoteを使ってアップされているところ自社のブログを使うことになってしまい、申し訳ございません。理由は複数ありますが、今回の記事とは無関係ですので省略させていただければと思います。
ノーコードやローコードのツールは私にみたいなプログラミングできない人にとって、あるいは、簡単に仕事をシェアしたいと思っている人にとってとてもありがたいものだと思います。
一方で、ノーコードやローコードでは、細かい設定や独自のカスタマイズがしづらい、いわゆる自由度が低いというデメリットを抱えざるをえないような気もしていまして、このような悩みを抱えられる方もいっらっしゃると考えております。
そんな状況で、kintoneというツールは、基本的にノーコードではあるものの、拡張性に極めて強く、さまざまなツールと連携でき、何より、GAFAMなどの海外テック企業ではなく、日本国内で発展しているもので、日本語でのサポートが充実しているという点で、これ以上ないサービスかと思います。
加えて、多くの企業が導入しており、ファンも多く、拡張機能のプラグインも多いことから今後の発展も期待できるプロダクトだと認識しておりますし、そうなってほしいと願っています。
しかしながら、これは専門家に怒られることを覚悟で言うと、生成AIの急速な進化によって、プログラミング自体が民主化されて、ある程度のアプリやツール、ウェブサイトが簡単に作れるようになってきています。
ただ、ベースがないツールをいきなり使うのはリスクがある一方で、まったくのノーコードだけじゃ機能面や見た目などでやや使いづらい、そんな状況でも、kintoneは独自のプログラミングで拡張できる余地を残しており、両方の折衷案をうまく兼ね備えたものだと考えます。
そんなkintoneの強力な連携ツールであるトヨクモ製品フォームブリッジについて、今年、生成AIを使いながら解決した事例について紹介したいと思います。
なにが課題か?
今では一部、反省しておりますが、当初、フォームブリッジにCSSやJavaScriptでの拡張性があることに反対の立場といいますか、できるだけプログラミングを使わない汎用なものであってほしい、それは、プログラミングを全く知らない人へフォームの編集を頼むことができなくなるというデメリットが残ってしまうということにありました。
しかしながら、これは想像でしかないですが、kintoneベースでウェブフォームを作ると、Googleフォームで考慮されているものが使えないようなことがいくつかでてしまうのか、例えば、簡単なところで言いますと、ラジオボタンを初期値で外すのにJavaScriptで制御しないといけない仕様(kintoneでは必須でどれかにチェックつくようになっている)となっています。
何が問題か?
例えば、性別を聞くような場合、「男性」、「女性」、「答えたくない」などを選ぶ設問でがあった時、「男性」、あるいは「女性」に初期値で入っていると、いろいろと問題になりそうですし、無意識に設問をすっ飛ばして、答えたつもりになっていた場合、本来答えるものと違う回答になる可能性があります。
ちなみに、以下は参考のコードで、フィールドコードを以下のところにカンマ(,)をつないで入れるとそれらのラジオボタンの設問は、初期値で外れて、かつ、ステップフォームを使っている場合、次のページへ行って、戻った時などにもチェックが外れないコードになっています。
(function () {
"use strict";
let confirm = false;
fb.events.form.mounted.push(function (state) {
if (!confirm) return;
let count = state.form.stepSize - 1;
while (count) {
document.querySelector(".fb-next").click();
count--;
}
});
fb.events.confirm.mounted.push(function (state) {
confirm = true;
});
fb.events.form.created.push(function (state) {
if (confirm) return;
// 対象フィールドコードを配列で管理
const targetFields = [
"radio_button_01","radio_button_02"
];
// 各フィールドの値をリセット
targetFields.forEach((field) => {
if (state.record[field]) {
state.record[field].value = "";
}
});
return state;
});
})();
上記のような課題がいくつかありまして、その一部を挙げたいと思います。
1.「その他」の選択肢
ラジオボタンやドロップダウン、チェックボックスなどで、「その他」という項目を作った場合、その他の具体的な内容を入力して欲しい場合があります。
これは、フォームブリッジの条件分岐を使えばできる話ではありますが、Googleフォームでは「その他を追加」というものにチェックを入れればできることから、そのくらいのレベルでできてくれるとありがたいという話になります。
2.項目をランダムに表示できない
ラジオボタンやチェックボックスの項目がランダムに表示できないということです。
もちろん課題とまでは言うことはないですが、Googleフォームでは選択肢をシャッフルするというオプションがチェックひとつでできる以上、できればその感覚はあってもいいのかなというレベルです。
ランダム表示できないと何が問題か?
これは心理学的な要素で、上から、あるいは、左からなど、最初の項目に影響を受けやすいということがあるため、アンケートなどで統計的な偏りをなくすためには、できればランダムすることを推奨しているためです。
3.排他制御ができない
これはGoogleフォームでもできるものではないですが、チェックボックスで複数選択できるのですが、ある特定の項目にチェックを入れた場合には、それ以外のチェックを外して、それ以外を選択できないようにすることです。
ただ、これに関しても、例えば、その前の項目でラジオボタンでそれ以外に該当するかを聞いて、条件分岐で次のチェックボックスの画面を出すかを設定すればいいことですが、できれば同一にやりたいところではあります。
4.選択肢の順位付けができない
これもGoogleフォームでできるものではないですが、これは例を挙げた方がわかりやすいと思います。
例えば、フォームブリッジ、kViewer、プリントクリエイター、kMailer、kBackup、DataCollectのどれが好きですか?3番目まで答えて欲しいという場合、皆さんはどうしますか?
1番目の設問は簡単で、そのままラジオボタンなどで選択してもらえばいいです。
2番目はどうでしょう?仮に1番目にフォームブリッジを選択した場合、2番目の回答ではフォームブリッジは出てきてほしくなさそうです。3番目も同様です。
選択肢が少ない場合、条件分岐を駆使すればできないことはないですが、選択肢が多い場合お手上げです。
5.チェックボックスで選択したうちの選択肢を作れない
これはただの期待というか、できればすごいというもので、課題ではないです。
これも例を挙げた方がわかりやすいと思います。例えば、フォームブリッジ、kViewer、プリントクリエイター、kMailer、kBackup、DataCollectの好きなものをいくつでも選んでよいというチェックボックス形式にしたとします。
その後、その中でどれが一番推しか?という設問を作りたい場合どうなるでしょう?
これも現状どうすればよいか私は考えつかなかったです。
どうするか?というか、どうしたか?
今回、上記のような課題を解決すべく、お遊び感覚で話題の生成AI(ChatGPT、Geminiなど)にダメもとで聞いてみました。
すると難しいと勝手に考えていた上記の課題がなんとなく解決したので、項目ごとにそれをシェアしたいと思います。
1.「その他」の選択肢
これは、上記でも触れましたが、しばらくは条件分岐で対応しましょう。
2.項目をランダムに表示する方法
フォームブリッジにおいて、項目をランダムに表示するにはどうすればいいのかと純粋に生成AIに聞いてみました。
テストし過ぎて最初なんてプロンプトを打ったのか全く覚えてないですが、最終的に以下のようなコードに行きつきました。
// ランダム化設定のフィールドコードと固定インデックスを管理
const randomizationSettings = {
check_box_01: [11],
check_box_02: [15],
};
// ランダム化の設定をフィールドに一括適用
Object.entries(randomizationSettings).forEach(([fieldCode, fixedIndices]) => {
if (!fb.events.fields[fieldCode]) fb.events.fields[fieldCode] = {};
fb.events.fields[fieldCode].mounted = [
function (state) {
randomizeRadioOptionsWithFixed(fieldCode, fixedIndices);
return state;
},
];
});
// 配列をシャッフルする関数
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// ランダム化しつつ指定のインデックスを固定する関数
function randomizeRadioOptionsWithFixed(fieldCode, fixedIndices) {
const radioContainer = fb.getElementByCode(fieldCode);
if (radioContainer) {
const radioElements = Array.from(radioContainer.querySelectorAll(".field"));
// 固定する要素を取り出し、ランダム化対象と分離
const fixedElements = fixedIndices.map((index) => radioElements[index]);
const randomizableElements = radioElements.filter(
(_, index) => !fixedIndices.includes(index)
);
// ランダム化対象をシャッフル
const shuffledElements = shuffleArray(randomizableElements);
// ランダム化した要素と固定要素を結合
const finalOrder = [...shuffledElements, ...fixedElements];
// 親コンテナに順に再追加
finalOrder.forEach((element) => {
radioContainer.appendChild(element);
});
}
}
上記のコードは、はっきり言ってなにしているのか、正確によくわかりません(笑)が、意外とシンプルなコードな気がします。
ただ、上記の3,4行目のチェックボックスのフィールドコードと、ブラケット内([ ])の数値を理解すれば簡単で、あるチェックボックスで、数番目の項目(優先順位に影響がなさそうなその他など)以外はランダムにしたいなどの場合、上記の例ではcheck_box_01のフィールドコードでは12番目の項目以外をランダムにして、check_box_02のフィールドコードでは16番目の項目以外をランダムにするということが可能になります。
3.排他制御をする方法
続いて、フォームブリッジにおいて、排他制御をするにはどうすればいいのかと純粋に生成AIに聞いてみました。
最終的に、以下のコードで対応できました。
function setupExclusiveCheckbox(fieldCode, targetIndex) {
const checkboxContainer = fb.getElementByCode(fieldCode);
if (checkboxContainer) {
const checkboxes = Array.from(
checkboxContainer.querySelectorAll(".field input[type='checkbox']")
);
checkboxes.forEach((checkbox, index) => {
checkbox.addEventListener("change", () => {
requestAnimationFrame(() => {
// ターゲットのチェックボックスがチェックされたか確認
const isTargetChecked = checkboxes[targetIndex].checked;
// 他のチェックボックスの表示状態を更新
checkboxes.forEach((cb, idx) => {
cb.closest(".field").style.display =
idx !== targetIndex && isTargetChecked ? "none" : "";
});
});
});
});
}
}
// フィールドコードとターゲットインデックスを配列で設定
const fieldConfigs = [
{ fieldCode: "check_box_01", targetIndex: 5 }, // "その他" -> 6番目
{ fieldCode: "check_box_02", targetIndex: 10 }, // "その他" -> 11番目
];
// 配列を基にmountedイベントを設定
fieldConfigs.forEach(({ fieldCode, targetIndex }) => {
fb.events.fields[fieldCode].mounted = [
function (state) {
setupExclusiveCheckbox(fieldCode, targetIndex);
return state;
},
];
});
上記のフィールドコードとtargetIndexの数値を修正することで、なんちゃって排他制御が可能となります。こちらも意外とシンプルな気がします。
例えば、上記では、check_box_01の選択肢の6個目に「その他」という選択肢がありますが、こちらはランダムにする必要はなさそうです。ですので、それ以外の項目をランダムにするため上記ではそのような設定にしております。同様にcheck_box_02では、11番目の項目に「その他」がありますので、targetIndexは10となっております。
4.選択肢の順位付けする方法
続いて、優先順位を付けたうえで回答するにはどうすればいいのかを生成AIに聞いてみました。
最終的に、以下のコードで対応できました。
(function () {
"use strict";
// 複数のフィールドコードグループを配列で管理
const fieldGroups = [
["radio_button_01", "radio_button_02", "radio_button_03"],
["radio_button_11", "radio_button_12", "radio_button_13"],
];
// 各フィールドグループに対して変更イベントを登録
fieldGroups.forEach((group) => {
group.forEach((fieldCode) => {
fb.events.fields[fieldCode].changed = [
(state) => {
updateRadioGroups(state, group);
return state;
},
];
});
});
// 選択肢を更新する関数
function updateRadioGroups(state, group) {
const selectedValues = group.reduce((acc, fieldCode) => {
acc[fieldCode] = state.record[fieldCode]?.value || null;
return acc;
}, {});
group.forEach((fieldCode) => {
const radioContainer = fb.getElementByCode(fieldCode);
if (!radioContainer) return;
const radioElements = radioContainer.querySelectorAll("label");
const currentValue = selectedValues[fieldCode];
radioElements.forEach((radioElement) => {
const value = radioElement.innerText.trim();
// 表示・非表示の処理
radioElement.style.display =
value !== currentValue &&
Object.values(selectedValues).includes(value)
? "none"
: "";
});
});
}
})();
上記は、”radio_button_01″, “radio_button_02”, “radio_button_03″のラジオボタンのグループのそれぞれの選択項目が干渉しないような形になっています。
5.チェックボックスで選択したうちの選択肢を表示する方法
最後に、チェックボックスで選んだ項目をラジオボタンに反映させるにはどうすれば良いか聞いてみました。
最終的に、以下のコードで対応できました。
fb.events.fields.check_box_01.changed = [
function (state) {
updateRadio(state, "check_box_01", "radio_button_01");
return state;
},
];
function updateRadio(state, checkboxField, radioField) {
requestAnimationFrame(() => {
const checkboxValues = state.record[checkboxField].value;
const radioContainer = fb.getElementByCode(radioField);
if (radioContainer) {
const radioElements = radioContainer.querySelectorAll(".field");
radioElements.forEach((radioElement) => {
radioElement.style.display = checkboxValues.includes(
radioElement.innerText.trim()
)
? ""
: "none";
});
}
});
}
上記で、”check_box_01″のフィールドコードで選択した項目が”radio_button_01″のフィールドコードのラジオボタンに反映されます。
実際にお試しできるフォーム
以下のフォームで実際にお試しできますので、ぜひ回答してみてください。
最後にアンケート結果を閲覧できるようにkViewerでご確認いただけます。
さいごに
いかがだったでしょうか。
今回、生成AIとの対話ということで、実際に使ったものとしては、無料版のChatGPTとGeminiでした。
ChatGPTはかなり丁寧にプログラムを記載してくれましたが、Geminiは教育系なのか、プログラムを省略しがちでコピペで済むような回答は得られないので、本当にプログラミングがわからない方はChatGPTをお勧めいたします。
また、今回生成AIに作ってもらったJavaScriptカスタマイズで実践したものは、中身がわからないのでちょっとした変更などでできなくなったり、見た目はなんとなくそれっぽいですが、なんちゃって状態で完璧な状態ではなかったりします。
なので、完全にノーコードで問題ないかと問われたら、現段階ではやや厳しい気もしていますが、今後はやりたいことがうまく実現できるコードを生成してくれるかもしれません。
もしくは、トヨクモさんがすべてを解決してくれるかもしれませんので、それも期待したいと思います!