【kintoneカスタマイズ】テーブル内の空白禁止・重複禁止

 アプリに少しづつ機能を足すのが楽しくなったので、もう少し続けてみます。
 前回の記事はこちら。

前提となる業務の流れ

 日々の測定データを記録していくんですが、現在の測定履歴は、1日に複数のデータが記録できますし、空白も許容されています。もちろん複数回記録することもあるかも知れませんが、それなら「日時」で記録するべきですね。
 今回のアプリでは、そうではなく、「1日に複数回記録したときは、最新の情報を記録する(当日のデータがすでにあった場合は、そのデータを修正する)」こととして、「同じ日のデータが複数存在してはいけない」というルールを仮定します。
 また、測定日の記入は必須とし、測定日が空白のデータの保存は認めないこととします。

重複と空白を禁止したい

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

 既に先程の画像にヒントが有るのですが……、「あれ?空白を禁止って、『必須項目にする』にチェックすればいいんじゃ?」


測定日フィールドのの設定

 正解です!カスタマイズする前に、カスタマイズ以外で解決できないか考えることって大切ですよね!
 ただ、今回は折角なので、「必須項目にする」をチェックしない場合のやり方を考えてみます(独自のエラーメッセージが表示できることが違いかな?)。

 「なーんだ、『値の重複を禁止する』もチェックすれば、カスタマイズ不要じゃん!」という訳でやってみます。

エラーが出て更新できない

 残念!エラーになっちゃいます。そう、テーブル内では重複禁止の設定ができないんです。という訳で、カスタマイズでなんとかしてみましょう。

どんな仕組みだったらいいんだろう?

色々方法はありそうですが、今までのカスタマイズと共存する前提で、楽な方法を考えてみます。

  •  「測定日」のデータの変更をトリガーにチェックするか……、あ、でも、このトリガー、既に並び替えで使ってるな。同じトリガーにまとめてもいいんだけど、「レコードの保存前」にチェックすれば事足りるんじゃないかな?

  •  複数行あるときに、それぞれの「測定値」をチェックするのって、少し面倒な気が……、なんかサクッとチェックする方法ないかな?

という訳で、既存のコードにできるだけ手を加えずに済む方法で考えてみることにしました。

作ったコード

 こんな感じになりました。

(() => {
  ('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',
    ],
    (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;
    },
  );

  // 保存前のチェック(エラーがあったら保存できない)
  kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], (event) => {
    const record = event.record;
    const table = record['測定履歴'].value;

    // 療養履歴の測定日がnullのデータがある
    if (typeof table.find((obj) => !obj.value.測定日.value) !== 'undefined') {
      event.error = '「療養履歴」に「測定日」が空白のデータがあります。';
    }

    // 療養履歴の測定日に重複データがある
    if (new Set(table.map((obj) => obj.value.測定日.value)).size != table.length) {
      event.error = '「療養履歴」に「測定日」が重複したデータがあります。';
    }

    return event;
  });
})();

増えた部分は、

  // 保存前のチェック(エラーがあったら保存できない)
  kintone.events.on(
    ['app.record.create.submit', 'app.record.edit.submit'],
    (event) => {
      const record = event.record;
      const table = record['測定履歴'].value;

     // 療養履歴の測定日がnullのデータがある
     if (typeof table.find((obj) => !obj.value.測定日.value) !== 'undefined') {
       event.error = '「療養履歴」に「測定日」が空白のデータがあります。';
     }

      // 療養履歴の測定日に重複データがある
      if (
        new Set(table.map((obj) => obj.value.測定日.value)).size != table.length
      ) {
        event.error = '「療養履歴」に「測定日」が重複したデータがあります。';
      }

      return event;
    }
  );

この部分だけです。トリガーとなるイベントを別にしたんで、すごくシンプルです。
 エラーか否かをチェックし、エラーのときはevent.errorにエラーメッセージを格納します。リターンしたときにエラーメッセージがあると、エラーメッセージを表示して保存がキャンセルされます(エラーを解消するまで保存できません)。

エラー

こんな感じです。
 各フィールドにもerrorでエラーメッセージを表示することが可能ですが、そのためには「どこがエラーか」を特定する必要があるので少し面倒です。今回は、シンプルにするためにアプリのエラーとして表示しました。こんな風に、「必要以上に複雑にしない」ことも大切かなと個人的に思ってます。

 では、それぞれの判定の解説です。

空白判定

      // 療養履歴の測定日がnullのデータがある
      if (typeof table.find((obj) => !obj.value.測定日.value) !== 'undefined') {
        event.error = '「療養履歴」に「測定日」が空白のデータがあります。';
      }

 前回と同じように「療養履歴」をtableに読み込んでから、findを使って測定値が空の行を検索してます(もし測定日が空白の行があれば、最初に見つかった行が返されるので「undefinedではない」が成立する)。

重複判定

      // 療養履歴の測定日に重複データがある
      if (
        new Set(table.map((obj) => obj.value.測定日.value)).size != table.length
      ) {
        event.error = '「療養履歴」に「測定日」が重複したデータがあります。';
      }

 こちらのほうが少し複雑かな?Set()は、配列を渡すと重複を排除して格納される便利なコレクションです。
(あ、この辺りから、用語の使い方に自信がありませんw、考えるな感じろ!って感じで、雰囲気を察していただければ助かります。)
 左辺は、各行の測定日を配列に格納して、Setで重複を排除してから個数を数えてます。
 右辺は療養履歴の行数なので、重複があれば左辺の数字が小さくなって右辺と合わなくなるという仕組みです。
 右辺と左辺で似たようなものなのに左辺はsize、右辺はlength、難しいですねぇ。この辺をサクッとソラで書けるようになれば、理解が進んできてるってことなのかな?

ここまでのアプリ

 今回までのカスタマイズを反映させたアプリをおいておきます。JavaScriptが含まれていますので、ご注意ください。
 個人的には、まぁまぁ需要がありそうな処理の詰め合わせになってるんじゃないかと思いますが、どうでしょう?

この記事が気に入ったらサポートをしてみませんか?