フォームブリッジの時刻フィールドに入力制御を実装する方法

先日FormBridgeの時刻フィールドを15分単位で表示する方法について調査し、本コミュニティの投稿を参考にさせていただきました。

結論として現在のFormBridgeの仕様では「時刻の選択肢を15分単位に制限して表示する」ことは難しいようです
(以前のバージョンでは対応可能だったとの情報もありました)。

そこで代替案として「選択された時刻が指定した分単位(例:15分)でない場合にエラーメッセージを表示する」入力制御を実装しました。

この方法であれば表示自体は制御できないものの、意図しない入力を防ぐことが可能です。

同様の課題でお困りの方の参考になれば幸いです。

以下にサンプルコードを掲載します。

※分単位は15分以外にも、30分など任意に変更可能です。
※2行目・3行目の「フィールドコード」と「分単位」を設定し、FormBridgeに読み込むだけで使用できます。

// ===== 設定ここから =====
const FIELD_CODE = '時刻';  // 対象フィールドコード
const UNIT_MINUTES = 15;       // 分単位(例:15, 30 など)
// ===== 設定ここまで =====

// フィールド変更イベント
formBridge.events.on(`form.field.change.${FIELD_CODE}`, function (context) {

    const timeValue = context.value;

    // 未入力対策
    if (!timeValue) return;

    // 分を取得
    const minute = parseInt(timeValue.split(':')[1], 10);

    // 指定分単位チェック
    if (minute % UNIT_MINUTES !== 0) {
        context.setFieldValueError(
            FIELD_CODE,
            `${UNIT_MINUTES}分単位で入力してください`
        );
    } else {
        context.setFieldValueError(FIELD_CODE, null);
    }
});
「いいね!」 1

別解として
hourmin

1. kintone側フィールドを作成する

フォームに以下の3つのフィールドを作成します。

時(hour)

フィールド名:任意
フィールドコード:hour
フィールド種別:ドロップダウン
※kintoneに保存しない場合、項目と順番は利用しないので、デフォルトのsample1とsample2のままでよい。今回の選択肢はJavaScriptで生成する
kintoneに保存する場合は、00-23まで選択肢をつくる必要がある。(時と分を結合されたものは保存されるので不要か?)

分(min)

フィールド名:任意
フィールドコード:min
フィールド種別:ドロップダウン
※kintoneに保存しない場合、項目と順番は利用しないので、デフォルトのsample1とsample2のままでよい。今回の選択肢はJavaScriptで生成する
kintoneに保存する場合は、00,15,30,45の選択肢をつくる必要がある。
(時と分を結合されたものは保存されるので不要か?)

保存用時刻(time_field)

フィールド名:任意
フィールドコード:time_field
フィールド種別:時刻フィールド

2. Formbridge側

2-1 Formbridge側でtime_fieldは、編集不可にしておく(ユーザーが任意の時間に変更するのを防ぐため)

2-2

以下のカスタマイズを適用する

const buildOptions = (start, end) =>
  Array.from({ length: end - start + 1 }, (_, i) =>
    String(start + i).padStart(2, '0')
  );

const buildTime = (hour, min) => {
  if (hour === '' || min === '' || hour == null || min == null) {
    return null;
  }

  const hh = String(hour).padStart(2, '0');
  const mm = String(min).padStart(2, '0');
  return `${hh}:${mm}`;
};

const syncTimeField = (context, changedFieldCode = null) => {
  const record = formBridge.fn.getRecord();

  const hour =
    changedFieldCode === 'hour'
      ? context.value ?? ''
      : record.hour?.value ?? '';

  const min =
    changedFieldCode === 'min'
      ? context.value ?? ''
      : record.min?.value ?? '';

  context.setFieldValue('time_field', buildTime(hour, min));
};

formBridge.events.on('form.show', (context) => {
  // hour を 00〜23 に設定
  formBridge.fn.updateFieldSetting('hour', 'options', buildOptions(0, 23));

  // min を 15分単位に設定
  formBridge.fn.updateFieldSetting('min', 'options', ['00', '15', '30', '45']);

  // 初期表示時に既存値から time_field を同期
  syncTimeField(context);
});

formBridge.events.on('form.field.change.hour', (context) => {
  syncTimeField(context, 'hour');
});

formBridge.events.on('form.field.change.min', (context) => {
  syncTimeField(context, 'min');
});

// 念のため送信時にも最終同期
formBridge.events.on('form.submit', (context) => {
  const record = formBridge.fn.getRecord();
  const hour = record.hour?.value ?? '';
  const min = record.min?.value ?? '';

  context.setFieldValue('time_field', buildTime(hour, min));
});
「いいね!」 2