kintoneプラグインで「フィールドAを変更したらフィールドBへ値をコピーする」という定番の処理を実装したところ、フィールド制御は動くのに値のコピーだけが画面に反映されないという症状に遭遇しました。原因はkintoneイベントの仕様にあります。同じ罠を踏む人が多そうなので、切り分け方と修正パターンをまとめます。
症状
kintone.events.on(['app.record.create.change.start_date',
'app.record.edit.change.start_date'], async (event) => {
if (!(await someAsyncCheck())) return event; // ← ここが罠
event.record.end_date.value = event.record.start_date.value;
return event;
});
このコード、someAsyncCheck() を挟んだ途端に end_date への反映が効かなくなります。JSエラーは出ません。黙って効かないだけです。
原因: change イベントは戻り値の Promise を待たない
kintoneのイベントハンドラは、多くのイベント(submit や index.show など)で Promise を返すと解決を待ってくれます。ところが create.change.* / edit.change.*(フィールド値変更イベント)は例外で、Promise を待ちません。
ハンドラを async にすると、関数は即座に pending 状態の Promise を返します。kintoneはそれを待たずに変更前の event.record を使って描画を確定させるため、await の後で event.record を書き換えても手遅れ、というわけです。
切り分けのポイント: 「制御系は動くのに値だけ効かない」
厄介なのは、同じ async ハンドラ内でも挙動が割れることです。
| 処理 | asyncハンドラ内での挙動 |
|---|---|
event.record[x].value = ...(戻り値経由の反映) |
効かない(戻り値のPromiseが無視されるため) |
kintone.app.record.setFieldShown() など命令的API |
効く(呼んだ瞬間に副作用が走るため) |
「表示/非表示の切り替えは動いてるから、イベント自体は発火してるはず。なのに値コピーだけ死ぬ」——この切り分けになったら、まずこの仕様を疑ってください。
修正パターン
パターン1: ハンドラを同期に保つ(推奨)
非同期にしていた理由が「キャッシュ済みの値の確認」程度なら、同期読みに書き換えるのが最も確実です。
kintone.events.on([...changeEvents], (event) => { // asyncを外す
if (someCheckSync() === false) return event; // キャッシュを同期参照
event.record.end_date.value = event.record.start_date.value;
return event;
});
私たちのケースでは、ライセンス判定のfetch結果を毎回 await していたのを、事前に取得してキャッシュした値を同期参照する形(未取得時は素通し)に変更して解決しました。
パターン2: 非同期処理の結果は kintone.app.record.set() で書く
外部APIの取得結果を使うなどどうしても非同期が必要な場合は、ハンドラ自体は同期で終わらせ、取得完了後に kintone.app.record.set() で画面へ書き込みます。戻り値経由ではなく命令的APIなら、changeイベントの外からでも反映されます。
kintone.events.on([...changeEvents], (event) => {
fetchSomething(event.record.zip.value).then((addr) => {
const rec = kintone.app.record.get();
rec.record.address.value = addr;
kintone.app.record.set(rec);
});
return event; // ハンドラは同期で即返す
});
テストの落とし穴
もうひとつ実務的な注意を。自前のモックハーネスでイベントハンドラを await fn(event) と呼んでいると、このバグはテストをすり抜けます(モックはPromiseを待ってしまうので、実機で起きない反映が起きる)。私たちはモック側に「changeハンドラが thenable を返していないか」の検査を足して回帰を防ぐようにしました。
まとめ
-
create.change.*/edit.change.*はハンドラのPromiseを待たない(submit等とは挙動が違う) - asyncハンドラで
await後にevent.recordを書き換えても画面に反映されない(エラーも出ない) - 命令的API(setFieldShown等)は効くため「制御は動くのに値だけ効かない」切り分けになる
- 対処は「ハンドラの同期化」または「
kintone.app.record.set()への書き込み」
筆者はkintoneプラグイン配布サービス Plumeru を開発しています。この記事は自社プラグインの不具合調査(値の連動が編集画面で効かない)で確認した内容です。