コンテンツにスキップ
「プリザンター入門」発売中!

$p.events.before_validate

概要

バリデーションチェックを行う前に実行するメソッドの指定方法を説明します。項目に入力した値が正しいかをチェックするときに使用してください。

制限事項

  1. 複数のスクリプトを作成し各スクリプトの内容に本関数を記述すると、スクリプトが上書きされるため、最新のもの以外は動作しません。回避策は以下FAQを参照してください。

構文

JavaScript

$p.events.before_validate = function (args) {
    //処理内容
}
または
$p.events.before_validate_{data-action属性の値} = function (args) {
    //処理内容
}

※イベントを発生させるボタンのdata-action属性の値を明示的に記述したい場合は取得してください。

使用例

以下のサンプルコードをプリザンターのスクリプトへ設定し出力先は「編集」にした後、編集画面で数値Aに100を入力して「更新」ボタンを押したときのみ更新されます(当例ではdata-action属性の値であるUpdateを明示的に記載してます)。

JavaScript
$p.events.before_validate_Update = function (args) {
    return ($p.getControl('NumA').val() != 100
        ? false //falseのときは処理が止まり、更新されない
        : true  //trueのときは更新される
        );
}

サンプルコード

Note

コード内の【 ... 】は適宜修正してください。

1. 「タイトル+対象月+作成者」の組み合わせによる条件付き重複チェック

タイトルと対象日を記入後、サイト内に同じタイトル・対象日が属する月の既存レコードがないかを検索します。
自分自身が作成した組み合わせのみを重複とみなし、保存を中断します。他のユーザが同じ組み合わせを使っていても対象外です。

判定基準

作成者が自分 タイトル一致 対象日が同じ月内 判定
× - - 保存可
○ × - 保存可
○ ○ × 保存可
○ ○ ○ エラー

実務での使いどころ

経費申請や出張申請のように、同じ月に複数件の正当な申請があり得る業務で、操作ミスによる二重登録を防ぐのに使えます。
単純な「日付完全一致」ではなく「対象日が同じ月かどうか」で判定するため、「同じ内容(タイトル)の申請を、月内の別の日にもう一度出してしまう」という、日付ピンポイントの一致では拾えないミスも検出できます。
他の申請者が同じ月に同じタイトルの申請をすること自体は業務上問題にならないため対象外とし、あくまで「自分自身の操作ミス」だけを拾う仕様です。

本サンプルは、「対象日が属する月」という期間ベースの判定を組み込むことで、単純な文字列結合キーでは表現できない業務要件に対応しています。

Note

標準の「重複禁止」機能との併用は避けてください(1項目単位・全レコード対象で常時ブロックされるため、本サンプルの「期間ベースの判定」「自分自身に限定」が反映されません)。

事前準備

  1. テーブルにタイトル項目(標準のタイトル)と、日付項目(対象日、項目コード:DateA)を用意してください。
  2. スクリプトを登録します。
    1. 「テーブルの管理 > スクリプト」タブで「新規作成」を選択する。
    2. タイトル: 任意の名前(例:タイトル・対象月の重複チェック)
    3. スクリプト: 下記サンプルコードを貼り付け
    4. 出力先: 「新規作成」と「編集」の両方にチェック
// ===== 設定 =====
const SITE_ID = $p.siteId();
const TITLE_COLUMN = 'Title';
const DATE_COLUMN = 'DateA';
const OWNER_COLUMN = 'Owner';
const buildDuplicateMessage = (matchedId) => `ID:${matchedId}と同一の内容です。`;

// ===== 状態 =====
let hasDuplicate = false;      // 現在の入力内容が重複しているかどうか
let duplicateMessage = '';     // 表示するエラーメッセージ
let eventsBound = false;       // change イベントを登録済みかどうか(多重登録防止)

// 対象日の文字列("yyyy/MM/dd"等)から、その月の初日・末日を求める
function getMonthRange(dateStr) {
    if (!dateStr) {
        return null;
    }
    const [year, month] = dateStr.split('T')[0].split(/[/-]/).map(Number);
    const firstDay = new Date(year, month - 1, 1);
    const lastDay = new Date(year, month, 0);
    return { firstDay, lastDay };
}

// Date型を"yyyy/MM/dd"形式の文字列に変換する(検索条件の指定に使用)
function formatDate(date) {
    const parts = [date.getFullYear(), date.getMonth() + 1, date.getDate()];
    return parts.map((n, i) => i === 0 ? n : String(n).padStart(2, '0')).join('/');
}

// 1件のレコードが、入力中の内容と重複しているかを判定する
// 条件:作成者が自分、タイトルが一致、対象日が同じ月内
function isSameRecord(record, title, monthRange, currentUserId) {
    if (record[OWNER_COLUMN] !== currentUserId || record[TITLE_COLUMN] !== title) {
        return false;
    }
    const recordDateStr = record.DateHash ? record.DateHash[DATE_COLUMN] : null;
    if (!recordDateStr) {
        return false;
    }
    const [ry, rm, rd] = recordDateStr.split('T')[0].split(/[/-]/).map(Number);
    const recordDate = new Date(ry, rm - 1, rd);
    return recordDate >= monthRange.firstDay && recordDate <= monthRange.lastDay;
}

// タイトル・対象日を元に、サイト内に重複するレコードがないか検索する
// 結果は hasDuplicate / duplicateMessage に保存しておき、保存時の判定に使う
function checkDuplicate(ownRecordId) {
    const title = $p.getValue(TITLE_COLUMN);
    const targetDateStr = $p.getValue(DATE_COLUMN);
    const currentUserId = $p.userId();

    hasDuplicate = false;
    duplicateMessage = '';
    $p.clearMessage();

    const monthRange = getMonthRange(targetDateStr);
    if (!title || !monthRange) {
        return;
    }

    $p.apiGet({
        id: SITE_ID,
        data: {
            View: {
                ColumnFilterHash: {
                    Title: title,
                    [DATE_COLUMN]: `${formatDate(monthRange.firstDay)},${formatDate(monthRange.lastDay)}`
                }
            }
        },
        done: function (data) {
            const allRecords = (data && data.Response && data.Response.Data) || [];
            const exactMatches = allRecords.filter(function (record) {
                if (ownRecordId !== null && record.ResultId === ownRecordId) {
                    return false;
                }
                return isSameRecord(record, title, monthRange, currentUserId);
            });
            if (exactMatches.length > 0) {
                hasDuplicate = true;
                duplicateMessage = buildDuplicateMessage(exactMatches[0].ResultId);
                $p.setMessage('#Message', JSON.stringify({
                    Css: 'alert-error',
                    Text: duplicateMessage
                }));
            }
        },
        fail: function () {
            console.log('重複チェックのための検索に失敗しました。');
        }
    });
}

// 画面表示時:現在の内容で一度チェックし、以後はタイトル・対象日の変更のたびに再チェックする
$p.events.on_editor_load = function () {
    const action = $p.action();
    if (action !== 'new' && action !== 'edit') {
        return;
    }
    const ownRecordId = action === 'edit' ? $p.id() : null;

    checkDuplicate(ownRecordId);

    if (eventsBound) {
        return;
    }
    eventsBound = true;

    $p.on('change', TITLE_COLUMN, function () {
        checkDuplicate(ownRecordId);
    });
    $p.on('change', DATE_COLUMN, function () {
        checkDuplicate(ownRecordId);
    });
};

// 保存直前:重複していれば保存を中断する
$p.events.before_validate_Create =
$p.events.before_validate_Update = function (args) {
    if (hasDuplicate) {
        $p.clearMessage();
        $p.setMessage('#Message', JSON.stringify({
            Css: 'alert-error',
            Text: duplicateMessage
        }));
        return false;
    }
    return true;
};

検証データ

ID:2001 タイトル: 会議費 対象日: 2026/07/15 作成者: ユーザーA
ID:2002 タイトル: 交通費 対象日: 2026/08/03 作成者: ユーザーA
ID:2003 タイトル: 出張費 対象日: 2026/08/05 作成者: ユーザーB
ID:2004 タイトル: 交際費 対象日: 2026/08/10 作成者: ユーザーA

実行結果1(ユーザAがタイトル「会議費」・対象日「2026/08/28」と入力して保存)

メッセージは表示されない。
(自分の登録ID:2001とタイトルは一致するが、ID:2001は7月のため対象月が異なる。保存可能)

実行結果2(ユーザAがタイトル「開発費」・対象日「2026/08/10」と入力して保存)

メッセージは表示されない。
(自分の登録ID:2004と対象日は近いが、タイトルが「交際費」で一致しないため対象外。保存可能)

実行結果3(ユーザAがタイトル「出張費」・対象日「2026/08/05」と入力して保存)

メッセージは表示されない。
(ID:2003はタイトル・月とも一致するが、作成者がユーザBのため対象外。保存可能)

実行結果4(ユーザAがタイトル「交通費」・対象日「2026/08/20」と入力して保存)

ID:2002と同一の内容です。

(作成者・タイトルが一致し、対象日も8月内のため、エラーとなり保存が中断される)

関連情報

FAQ:$p.events.on_editor_loadを複数設定できるようにしたい