【kintoneカスタマイズ】テーブルが変更されたらデータを日付順に並べ替える

 前回の記事の発展で「測定履歴を測定日順に並び替える」という思いつきを書いたんですが、実際に挑戦してみました。これ、意外と強敵ですw
 妥当なやり方とはとても思えないのですが、とりあえず、自分の知識内でのやりかたです。

どんなカスタマイズをするのか?

「新規にレコードを記入するとき」「既存のデータを編集するとき」に「測定履歴の情報が追加・修正された」ことをトリガーに測定履歴を測定日の昇順に並び替える。

気をつけないといけないこと

 「測定履歴」はテーブルですが、テーブルのchangeイベントは、

テーブルの行を追加するボタンや削除するボタンをクリックしたときに、イベントが発生します。

https://cybozu.dev/ja/kintone/docs/js-api/events/edit/edit-change-event/

なんです。つまり、テーブル内の各フィールドの変更を察知するには、「測定履歴」ではなく、テーブルの中の各フィールドを指定する必要があります。

  kintone.events.on(
    [
      'app.record.create.change.測定日',
      'app.record.edit.change.測定日',
      'app.record.create.change.体温',
      'app.record.edit.change.体温',
      'app.record.create.change.SpO2',
      'app.record.edit.change.SpO2',
    ],

前回のこれですね。

 さて、テーブルの中を並び替えるということはどういうことか?言い換えれば、「テーブルの中身の各行のフィールドの値を変更する」ということになります。
 あれ?このカスタマイズのトリガーって「測定履歴の情報が追加・修正された」ときに発火するんじゃなかったっけ?そうなんです。return eventで反映された瞬間、変更部分の数だけ発火が積まれることになりますw
 もうこの時点でこのやり方は無理筋っぽい……素直に、ボタンを設置するなり、保存時に並び替えるなりすればいいんじゃない?って思うんですが、敢えて強行突破してみました。

作ったコード

こんな感じです。
あ、前回のコードを流用して、更に一つのファイルに纏めました。

(() => {
  ('use strict');

  // フィールドを編集不可にする
  kintone.events.on(['app.record.create.show', 'app.record.edit.show', 'app.record.index.edit.show'], (event) => {
    const items = ['測定日_最新', '体温_最新', 'SpO2_最新'];
    const record = event.record;
    items.forEach((item) => (record[item].disabled = true));
    return event;
  });

  // 最後尾のデータを最新情報としてコピーする
  kintone.events.on(
    [
      'app.record.create.change.測定日',
      'app.record.edit.change.測定日',
      'app.record.create.change.体温',
      'app.record.edit.change.体温',
      'app.record.create.change.SpO2',
      'app.record.edit.change.SpO2',
    ],
    function (event) {
      const record = event.record;
      const table = record['測定履歴'].value;
      const sortedTable = Array.from(table).sort((a, b) => {
        if ((!!a.value.測定日.value ? a.value.測定日.value : '1900-01-01') > (!!b.value.測定日.value ? b.value.測定日.value : '1900-01-01')) {
          return 1;
        } else {
          return -1;
        }
      });
      if (JSON.stringify(table.map((obj) => obj.value.測定日)) != JSON.stringify(sortedTable.map((obj) => obj.value.測定日))) {
        record['測定履歴'].value = sortedTable;
      }
      const targetRow = sortedTable[sortedTable.length - 1].value;
      record.測定日_最新.value = targetRow.測定日.value;
      record.体温_最新.value = targetRow.体温.value;
      record.SpO2_最新.value = targetRow.SpO2.value;
      return event;
    },
  );
})();

ポイントの説明

データのソート

      const record = event.record;
      const table = record['測定履歴'].value;
      const sortedTable = Array.from(table).sort((a, b) => {
        if ((!!a.value.測定日.value ? a.value.測定日.value : '1900-01-01') > (!!b.value.測定日.value ? b.value.測定日.value : '1900-01-01')) {
          return 1;
        } else {
          return -1;
        }
      });

 「測定履歴」の並び替えですが、「一度配列に落とし込んで、並び替えた後に、ごっそり配列の内容で置き換える」という手法をとることにしました。
 測定日の並び替えにあたっては、nullの場合に比較がうまくいかないので、三項演算子を使ってnullは'1900-01-01'に置き換えて並び替えを行っています(実際にフィールドに代入されるわけではなく、ソートのためだけに置き換えています)。

「測定履歴」の内容の差し替え

      if (JSON.stringify(table.map((obj) => obj.value.測定日)) != JSON.stringify(sortedTable.map((obj) => obj.value.測定日))) {
        record['測定履歴'].value = sortedTable;
      }

 置き換えるだけではなく、敢えてif文で囲っています。何をしているかというと、「測定日の並び替えが実際にあった場合のみ、測定履歴を差し替える」という内容です。
 なぜこんなことをするかというと、差し替えを行うと、それによって「修正されたフィールドの個数分のchangeイベントが発生」するからです。もし、このif文がないと、

  1. 発生したイベントでも無条件に測定履歴を更新

  2. 修正されたフィールドの個数分のchangeイベントが発生

  3. 以下ねずみ算……

で、最終的にエラーで止まってしまいます。
 ですので、このif文を使うことによって、無駄なchangeイベントの発生を1世代のみで止めている感じです。つまり、見た目上は一瞬でも、少なくとも1世代分の同じような処理が発生しているわけで、すごく消極的で泥臭い解決方法となっています。

測定日が並べ変わったかの確認


    JSON.stringify(table.map((obj) => obj.value.測定日))

 並びを確認するには配列を順番に比較する方法もありますが、今回はJSON.stringifyを使って文字列に変換して比較する形をとってみました。個人的にはコードとして趣旨が分かりやすい気がしますが、どうなんだろう?


いいなと思ったら応援しよう!