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

Featured Image(Photo) by Nik Shuliahin on Unsplash

こちらも合わせてお読みください。

更新履歴

  • 2021/8/26 記事を分割しました。
  • 2021/8/26 「弊社のQRコード生成プラグインの場合」を追加しました。

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

複数のカスタマイズ(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));
});

1: (4) [“type”, “appId”, “recordId”, “record”]
2: (2) [“id”, “revision”]

そのため、例えば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);
});

Uncaught TypeError: Cannot read property ‘$id’ of undefined

どうやら同一のイベントハンドラーが複数設定されている場合、先に実行されたイベントハンドラーの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);
  });

1: (3) [“type”, “appId”, “record”]
2: false

この状況に対応できる場合もあります。自社のカスタマイズの実行順が先の場合は対応しやすいです。前述の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;するのが良いはずなのでreturn false;には対応できる

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

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

これサイボウズさん側でどうにかならないですかね・・・。利用者もプラグイン開発者もとても困ります。せめてお作法としてcybozu developer networkに掲載してほしいです。

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

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

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

(() => {
  "use strict";

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

設定はこの通りです。

全体JS

アプリJS

プラグイン

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

全体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コード生成プラグインでエラーが発生する」という問い合わせです。これは「別のJSカスタマイズ」が先に動作し、「QRコード生成プラグイン」が後で動作していました。「別のJSカスタマイズ」では保存成功後イベントでレコードを更新しています。

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

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

この記事を書いた人