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 イベントは保存前に発火し、event.error
を設定することで保存をキャンセルできます。一方、submit.success
は保存が完了した後に発火するため、バリデーションには使用できません。submit.success
は通知の送信やリダイレクトなど、保存後の処理に使用します。
基本的なバリデーション
画面全体のエラーメッセージ
event.error にメッセージを設定すると、画面上部にエラーメッセージが表示され、保存がキャンセルされます。
(() => {
'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 にメッセージを設定すると、該当フィールドの直下にエラーメッセージが表示されます。
(() => {
'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 を設定しなくても保存はキャンセルされます。
複数フィールドの相関チェック
実務では、単一フィールドの検証だけでなく、複数フィールド間の整合性を検証するケースが多くあります。
(() => {
'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;
});
})();
非同期バリデーション
submit イベントは async/await に対応しているため、REST API を使った外部データとの照合が可能です。
(() => {
'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.show、edit.show、detail.show、change イベントでは async/await
が使えません。一方、submit イベントと submit.success イベントでは async/await
が使用可能です。
サブテーブルのバリデーション
サブテーブル内のデータに対してもバリデーションを実装できます。
(() => {
'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;
});
})();
バリデーションの共通化
複数の画面で同じバリデーションロジックを使いたい場合は、バリデーション関数を分離することで管理しやすくなります。
(() => {
'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 イベントで実装します。
(() => {
'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.submitとapp.record.edit.submitイベントで保存前バリデーションを実装できるevent.errorで画面全体のエラーメッセージ、record[フィールドコード].errorでフィールド単位のエラーメッセージを表示できるsubmitイベントはasync/awaitに対応しており、REST API を使った非同期バリデーションが可能- サブテーブル内のデータに対しても行単位のバリデーションを実装できる
- バリデーション関数を分離することで、複数画面での再利用性が向上する
submit.successイベントは保存成功後の処理(通知・リダイレクト等)に使用する