Skip to content

kintone カスタマイズのつまづきポイント〜カスタマイズの競合〜

複数カスタマイズで同一イベントハンドラーを利用する場合イベントハンドラーの引数が仕様と異なる場合がある

複数のカスタマイズ(JavaScript カスタマイズとプラグイン)で同一イベントハンドラーを利用する場合、後に実行されるカスタマイズでエラーが発生する可能性があります。例えば次のコードを実行すると 2 つ目のイベントハンドラーの引数(event 変数)の内容が仕様と異なることがわかります。

kintone.events.on("app.record.create.submit.success", (event) => {
console.log("1:", Object.keys(event));
return kintone.api("/k/v1/record", "POST", {
app: kintone.app.getId(),
record: {}
});
});
kintone.events.on("app.record.create.submit.success", (event) => {
console.log("2:", Object.keys(event));
});

そのため、例えば record プロパティを参照しているとエラーが発生します。

kintone.events.on("app.record.create.submit.success", (event) => {
console.log("1:", event.record.$id.value);
return kintone.api("/k/v1/record", "POST", {
app: kintone.app.getId(),
record: {}
});
});
kintone.events.on("app.record.create.submit.success", (event) => {
console.log("2:", event.record.$id.value);
});

どうやら同一のイベントハンドラーが複数設定されている場合、先に実行されたイベントハンドラーの return 値が後に実行されるイベントハンドラーの引数にそのまま?渡されるという仕様のようです。参考までにイベントをキャンセルできる return false; の場合は false が渡されてそのまま処理は継続します。

kintone.events.on("app.record.create.submit", (event) => {
console.log("1:", Object.keys(event));
return false;
});
kintone.events.on("app.record.create.submit", (event) => {
console.log("2:", event);
});

この状況に対応できる場合もあります。自社のカスタマイズの実行順が先の場合は対応しやすいです。前述の app.record.create.submit.success の場合は return Promise; せずに適切なオブジェクトを用意して return すれば、実行順が後のカスタマイズも動作します。具体的な手順は次のとおりです。

  1. return Promise;しない
  2. 変数を作る
  3. 変数にプロパティ(type, appId, recordId, record)をセットする
  4. 変数を return する

自社のカスタマイズの実行順が後の場合や先で return false; したい場合は自社側だけで対応するのは難しいことが多いです。

実行順が後の場合、先に動作したイベントハンドラーが return Promise; を実行したら適切なオブジェクトを作れない可能性が高いからです(Promise の内容による)。

実行順が先で return false; したい場合は後に動作するカスタマイズでも return false; してもらう必要があります。

そして自社のカスタマイズが先に実行されるのか、後に実行されるのかはわかりません。アプリ A では自社のカスタマイズが先に動作し、アプリ B では後に動作することもあります。

カスタマイズの実行順序に関する公式見解はない

さて、上の問題に対応するためにはカスタマイズの実行順序が重要ですが、これに関する公式見解はありません(私は見つけられませんでした)。cybozu developer network のコミュニティに「kintone の全体 JS、アプリ JS、プラグイン JS の読み込み順を調べてみた」という内容が投稿されています。現状こういう仕様というだけで保証はできないのでしょう。

上記の投稿では全体 JS、アプリの JS、プラグインの実行順序だけで、それぞれが複数登録されている場合の順序はわからなかったので確認しました。確認に利用したのは次のコードです。

(() => {
"use strict";
kintone.events.on("app.record.detail.show", (event) => {
console.log("1つ目の全体JS"); // ここはスクリプトごとに変更
});
})();

設定はこのとおりです。

全体JS

main1

アプリJS

main2

プラグイン

main3

結果はこのようになりました。

main4

全体 JS とアプリ JS は並び替えれば実行順序が変わります。

プラグインは少し複雑でアプリ設定のプラグイン画面で上のものから先に実行されていきます。この並びは「kintone システム管理 > プラグイン」の並び順に依存し、kintone システム管理で後に登録したものが上になります。つまり、プラグインは kintone システム管理で後に登録したものが先に実行されます。

プラグインの実行順序を変更するには「kintone システム管理 > プラグイン」でプラグインファイルの再読み込みが必要です。再読み込みをすると他のアプリでもプラグインの実行順所が変わります。この作業が別の問題を引き起こす可能性があるので注意が必要です。

弊社の QRコード生成プラグインの場合

スペースフィールドに表示する機能と添付ファイルフィールドに保存する 2 つの機能があります。後者は保存成功後イベント(app.record.create.submit.success)を利用しています。

保存成功前ではなく保存成功後を利用しているのは次の理由です。

  • 保存成功前イベントではレコード番号がまだない
  • 保存成功前イベントでは添付ファイルフィールドの情報が取得できない
  • QRコード生成プラグインの処理がエラーになった場合もレコード保存はエラーにしたくない

これまでに 2 種類の問い合わせをいただきました。

1 つ目は「別のプラグインでエラーが発生する」という問い合わせです。これは「QRコード生成プラグイン」が先に動作し、「別のプラグイン」が後で動作していました。原因は QRコード生成プラグインの次のコードです。

kintone.events.on(["app.record.create.submit.success"], async (event) => {
return kintone.api(kintone.api.url("/k/v1/record.json", true), "PUT", params);
});

developer network で推奨されている書き方ではありますが、別のプラグインの保存実行後イベントが受け取る event オブジェクトの内容がリビジョンだけになってしまうのが原因です。

これには対応済みで、次のようなコードで対応できます。

kintone.events.on(["app.record.create.submit.success"], async (event) => {
const res = await kintone.api(
kintone.api.url("/k/v1/record.json", true),
"PUT",
putParams
);
// レコードを取得
const record = await kintone.api(
kintone.api.url("/k/v1/record.json", true),
"GET",
getParams
);
// returnするオブジェクトを作成する
const newEvent = { ...event, record };
return newEvent;
});

2 つ目は「QRコード生成プラグインでエラーが発生する」という問い合わせです。これは「別の JavaScript カスタマイズ」が先に動作し、「QRコード生成プラグイン」が後で動作していました。「別の JavaScript カスタマイズ」では保存成功後イベントでレコードを更新しています。

先に動作しているのが JavaScript カスタマイズではなくプラグインであればプラグインの読み込み順の変更で対応できるかもしれません。(他のアプリに影響する可能性があるのでお勧めしません)今回は JavaScript カスタマイズでしたのでどうやってもこの実行順序は変えられません。

レコード更新のレスポンスはリビジョンだけなので、QRコード生成プラグインが受け取る event オブジェクトの内容もリビジョンだけです。レコード番号も受け取れればレコード取得して、という対応が可能なのですが、そうではないので QRコード生成プラグインだけでは対応できません。