lightning:input の DatePicker は入力可能な日を制限できない場合がある


[祝GA] lightning:input の DatePicker は入力可能な日を制限できる という投稿をしておきながら、「お前は何を言ってるんだ?」という気がしなくもないですが、気付いてしまったのでメモしておきます

TL;DR

  • どんな場合?
    • Datepicker と他のフォーム (例えば SUBMIT ボタン) があるような場合
    • Datepicker にテキスト入力して SUBMIT すると、範囲外の日付を入力できてしまう (下の gif 参照)
  • 回避方法?
    • controller.js or helper.js で validation かなと

以下、時間がある人向け

どんな場合?

以下の gif を見ていただくと早いかと思いますが、Datepicker の入力範囲外 (グレーアウト) の 7/25 という値は、キーボード入力できてしまうのですね。。。
「範囲外は入力できないはず」と安心しきって SUBMIT ボタンのハンドラでバリデーションをかけていないと、範囲外の値が流れていってしまうことになります

ちなみに、ソースは以下です。

<!-- コンポーネント -->
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">

    <!-- body -->
    <div class="row">
        <h2 class="header">Datepicker</h2>
        <lightning:input type="date" aura:id="datepicker1" name="input1"
                         label="Date field with min and max values"
                         value="2018-07-10" min="2018-07-05" max="2018-07-20" />

        <!-- SUBMIT ボタン -->
        <div class="gutter"/>
        <lightning:button variant="brand" label="SUBMIT" onclick="{! c.handleClick }"/>

        <!-- SUBMIT ボタンクリックで日付を表示する -->
        <div class="gutter"/>               
        <div aura:id="msg">
            You entered: <ui:outputDate aura:id="oDate" value="" />
        </div>

    </div>

</aura:component>
// controller.js
({
  handleClick: function(cmp, event, helper) {
    // Datepicker の日付を取得して
    const value = cmp.find("datepicker1").get("v.value");
    // outputDate にセットする
    cmp.find("oDate").set("v.value", value);
  }
})
// style.css
.THIS {
    background-color: white;
    margin: 5rem;
    height: 25rem;
    padding: 2rem;
}

.THIS .gutter {
    margin-top: 2rem;
}

回避方法

lightning:inpu type="date"の属性でそれらしいものが見つからなかったので、地道にハンドラ (controller.js or helper.js) 内で validation が手っ取り早いかなと思います。

日付については moment.js (v2.22.2) のお世話になります。moment.min.js 16.4k を右クリックで保存して、静的リソースに momentjs のように入れました。

挙動はシンプルに「max を超えている場合は何もしない」ことにしました。

<!-- コンポーネント -->
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
    <!-- lib -->
    <ltng:require scripts="{!$Resource.momentjs}" />

    <!-- body -->
    <div class="row">
        <h2 class="header">Datepicker</h2>
        <lightning:input type="date" aura:id="datepicker1" name="input1"
                         label="Date field with min and max values"
                         value="2018-07-10" min="2018-07-05" max="2018-07-20" />

        <!-- SUBMIT ボタン -->
        <div class="gutter"/>
        <lightning:button variant="brand" label="SUBMIT" onclick="{! c.handleClick }"/>

        <!-- SUBMIT ボタンクリックで日付を表示する -->
        <div class="gutter"/>               
        <div aura:id="msg">
            You entered: <ui:outputDate aura:id="oDate" value="" />
        </div>

    </div>

</aura:component>
// controller.js
({
  handleClick: function(cmp, event, helper) {
    // 説明簡略化のため、max の値をここでも定義
    const maxDateObj = moment("2018-07-20");
    // 入力された日付を moment にセット
    const inputObj = moment(cmp.find("datepicker1").get("v.value"));

    // max を超えていないか判定
    if (maxDateObj.diff(inputObj) < 0) {
      // max 以降の日付が入力された場合、何もしない
      return;
    }

    // max 以内の日付であれば、反映する
    cmp.find("oDate").set("v.value", inputObj.format("YYYY-MM-DD"));
  }
})

2018-07-25 を入力した場合は、SUBMIT ボタン下の You entered: に反映されないことが確認できました。

まとめ

max 以降の日付が入力された場合の挙動を制御することができました。min についても同様です。
将来的に今回のような部分までケアされると良いですね

蛇足

本投稿を書きながら思ったのですが業務では
「範囲外が入力された時点で SUBMIT ボタンを非活性にする」
などという要件も考えられますね。。。

Datepicker からフォーカスが外れた (onblur) 場合と、値が更新された場合 (onchange) を活用すれば実現できるのですが・・・長くなってしまったので、余力があればまたいつか