submit イベントのバリデーション

にメンテナンス済み

kintone の標準機能でも必須項目の設定や値の範囲制限は可能ですが、「複数フィールドの組み合わせチェック」や「外部データとの整合性検証」など、業務ロジックに基づいた高度なバリデーションには JavaScript カスタマイズが必要です。

submit イベントを使えば、ユーザーが保存ボタンをクリックした直後、レコードが実際に保存される前の段階でバリデーション処理を実行できます。

この記事では、submit イベントの基本から、実践的なバリデーションパターンまで解説します。

submit イベントの種類

kintone には、レコード保存に関連する複数のイベントが用意されています。

イベント名発火タイミング用途
app.record.create.submit新規作成の保存ボタン押下時作成時バリデーション
app.record.edit.submit編集の保存ボタン押下時編集時バリデーション
app.record.index.edit.submit一覧編集の保存ボタン押下時一覧編集時バリデーション
app.record.create.submit.success新規作成の保存成功後保存後処理
app.record.edit.submit.success編集の保存成功後保存後処理
submit と submit.success の違い

submit イベントは保存前に発火し、event.error を設定することで保存をキャンセルできます。一方、submit.success は保存が完了した後に発火するため、バリデーションには使用できません。submit.success は通知の送信やリダイレクトなど、保存後の処理に使用します。

基本的なバリデーション

画面全体のエラーメッセージ

event.error にメッセージを設定すると、画面上部にエラーメッセージが表示され、保存がキャンセルされます。

submit-basic.js
(() => {
  'use strict';

  const events = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'mobile.app.record.create.submit',
    'mobile.app.record.edit.submit',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;

    // 開始日が終了日よりも後の場合はエラー
    const startDate = record['開始日'].value;
    const endDate = record['終了日'].value;

    if (startDate && endDate && startDate > endDate) {
      event.error = '開始日は終了日よりも前の日付を指定してください';
    }

    return event;
  });
})();

フィールド単位のエラーメッセージ

record[フィールドコード].error にメッセージを設定すると、該当フィールドの直下にエラーメッセージが表示されます。

submit-field-error.js
(() => {
  'use strict';

  const events = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'mobile.app.record.create.submit',
    'mobile.app.record.edit.submit',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;

    // メールアドレスの形式チェック
    const email = record['メールアドレス'].value;
    if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      record['メールアドレス'].error = '正しいメールアドレスの形式で入力してください';
    }

    // 電話番号の形式チェック
    const phone = record['電話番号'].value;
    if (phone && !/^[\d-]+$/.test(phone)) {
      record['電話番号'].error = '電話番号は半角数字とハイフンで入力してください';
    }

    return event;
  });
})();
チェック

フィールド単位のエラーと画面全体のエラーは併用できます。フィールド単位のエラーが 1 つでも設定されていれば、event.error を設定しなくても保存はキャンセルされます。

複数フィールドの相関チェック

実務では、単一フィールドの検証だけでなく、複数フィールド間の整合性を検証するケースが多くあります。

submit-cross-field.js
(() => {
  'use strict';

  const events = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'mobile.app.record.create.submit',
    'mobile.app.record.edit.submit',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;

    // パターン 1: 区分に応じた必須チェック
    const category = record['区分'].value;
    if (category === '法人') {
      if (!record['法人名'].value) {
        record['法人名'].error = '法人の場合は法人名を入力してください';
      }
      if (!record['法人番号'].value) {
        record['法人番号'].error = '法人の場合は法人番号を入力してください';
      }
    }

    // パターン 2: 金額の整合性チェック
    const subtotal = Number(record['小計'].value) || 0;
    const tax = Number(record['消費税'].value) || 0;
    const total = Number(record['合計'].value) || 0;

    if (total !== subtotal + tax) {
      record['合計'].error = '合計が小計と消費税の合算値と一致しません';
    }

    // パターン 3: チェックボックスと関連フィールドの検証
    const agreement = record['同意確認'].value;
    if (!agreement.includes('同意する')) {
      event.error = '同意確認にチェックを入れてください';
    }

    return event;
  });
})();
エラーハンドリング
JavaScriptはその柔軟性から、エラーにならない工夫が容易です。必要となって変数を定義したんであれば、うまく取得できない場合はエラーを出力するべきです。今回はkintoneのJavascriptカスタマイズにおけるエラー処理についてお

非同期バリデーション

submit イベントは async/await に対応しているため、REST API を使った外部データとの照合が可能です。

submit-async-validation.js
(() => {
  'use strict';

  /**
   * 指定した条件でレコードを取得する
   * @param { Object } params
   * @param { string | number } params.app - アプリID
   * @param { string } params.query - 検索条件
   * @returns { Promise<{ records: Record<string, any>[], totalCount: string | null }> }
   */
  const getRecords = (params) => {
    const { app, query } = params;
    return kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', {
      app,
      query,
      totalCount: true,
    });
  };

  const events = [
    'app.record.create.submit',
    'mobile.app.record.create.submit',
  ];

  kintone.events.on(events, async (event) => {
    const record = event.record;

    // 同じメールアドレスのレコードが既に存在するかチェック
    const email = record['メールアドレス'].value;
    if (email) {
      const query = `メールアドレス = "${email}"`;
      const { totalCount } = await getRecords({ app: kintone.app.getId(), query });

      if (Number(totalCount) > 0) {
        record['メールアドレス'].error = 'このメールアドレスは既に登録されています';
      }
    }

    return event;
  });
})();
非同期処理が使えないイベント

create.showedit.showdetail.showchange イベントでは async/await が使えません。一方、submit イベントと submit.success イベントでは async/await が使用可能です。

非同期処理(async/await)
JavaScriptカスタマイズを行う上でよく利用する非同期処理を実装する方法と、実装する上での注意点を紹介します。kintone.events.onに登録した関数は引数として受け取ったeventを最終的にreturnします。この関数を非同

サブテーブルのバリデーション

サブテーブル内のデータに対してもバリデーションを実装できます。

submit-subtable-validation.js
(() => {
  'use strict';

  const events = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'mobile.app.record.create.submit',
    'mobile.app.record.edit.submit',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;
    const table = record['明細テーブル'].value;

    // テーブルが空の場合はエラー
    if (table.length === 0) {
      event.error = '明細を1行以上入力してください';
      return event;
    }

    // 各行のバリデーション
    table.forEach((row, index) => {
      const quantity = Number(row.value['数量'].value) || 0;
      const unitPrice = Number(row.value['単価'].value) || 0;

      if (quantity <= 0) {
        row.value['数量'].error = '数量は1以上を入力してください';
      }

      if (unitPrice <= 0) {
        row.value['単価'].error = '単価は1以上を入力してください';
      }
    });

    // 合計金額の上限チェック
    const totalAmount = table.reduce((sum, row) => {
      const quantity = Number(row.value['数量'].value) || 0;
      const unitPrice = Number(row.value['単価'].value) || 0;
      return sum + quantity * unitPrice;
    }, 0);

    if (totalAmount > 10000000) {
      event.error = '合計金額が1,000万円を超えています。承認フローを確認してください';
    }

    return event;
  });
})();
テーブル操作の基本
kintoneのテーブル(サブテーブル)フィールドをJavaScriptカスタマイズで操作する方法を詳しく解説します。テーブルのデータ構造から、行の追加、更新、削除まで、実践的なサンプルコードと共に紹介します。

バリデーションの共通化

複数の画面で同じバリデーションロジックを使いたい場合は、バリデーション関数を分離することで管理しやすくなります。

submit-reusable.js
(() => {
  'use strict';

  /**
   * レコードのバリデーションを実行する
   * @param { Object } record - kintone レコードオブジェクト
   * @returns { string | null } エラーメッセージ(エラーなしの場合は null)
   */
  const validateRecord = (record) => {
    // 必須チェック
    const requiredFields = ['案件名', '担当者', '期限'];
    for (const fieldCode of requiredFields) {
      if (!record[fieldCode].value) {
        record[fieldCode].error = `${fieldCode}は必須です`;
      }
    }

    // 期限の過去日チェック
    const deadline = record['期限'].value;
    if (deadline) {
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      if (new Date(deadline) < today) {
        record['期限'].error = '過去の日付は指定できません';
      }
    }

    // 画面全体のエラー(フィールドエラーがある場合に表示)
    const hasFieldError = Object.values(record).some((field) => field.error);
    return hasFieldError ? '入力内容に誤りがあります。修正してください' : null;
  };

  const events = [
    'app.record.create.submit',
    'app.record.edit.submit',
    'mobile.app.record.create.submit',
    'mobile.app.record.edit.submit',
  ];

  kintone.events.on(events, (event) => {
    const error = validateRecord(event.record);
    if (error) {
      event.error = error;
    }
    return event;
  });
})();

submit.success イベントの活用

バリデーションではありませんが、保存成功後に実行したい処理は submit.success イベントで実装します。

submit-success.js
(() => {
  'use strict';

  const events = [
    'app.record.create.submit.success',
    'mobile.app.record.create.submit.success',
  ];

  kintone.events.on(events, (event) => {
    const record = event.record;
    const recordId = event.recordId;

    // 保存成功後にレコード詳細画面へ遷移
    const appId = kintone.app.getId();
    location.href = `/k/${appId}/show#record=${recordId}`;

    return event;
  });
})();
チェック

submit.success イベントでは、event.recordId でレコード ID を取得できます。新規作成の場合、保存後にレコード ID が確定するため、保存後の処理で利用する場合に便利です。

まとめ

  • app.record.create.submitapp.record.edit.submit イベントで保存前バリデーションを実装できる
  • event.error で画面全体のエラーメッセージ、record[フィールドコード].error でフィールド単位のエラーメッセージを表示できる
  • submit イベントは async/await に対応しており、REST API を使った非同期バリデーションが可能
  • サブテーブル内のデータに対しても行単位のバリデーションを実装できる
  • バリデーション関数を分離することで、複数画面での再利用性が向上する
  • submit.success イベントは保存成功後の処理(通知・リダイレクト等)に使用する

練習問題

submit イベントで保存をキャンセルする方法として正しいものはどれですか?

フィールド単位のエラーを表示する方法として正しいものはどれですか?
submit イベントで async/await は使用できますか?
#kintone #JavaScript #TypeScript