更新於 2024/11/14閱讀時間約 10 分鐘

在 Vue 中設定 input date 輸入的日期範圍

問題描述

  在設計輸入日期的表單時,有時我們會需要限定時間範圍。

  像是常見的網銀帳戶記錄,很多就有類似從今天開始前的一年內為查詢範圍的限制,有時候就算在說明文字中已說明資料範圍僅有一年內的資料,還是有可能會有用戶去選到超過一年內的日期。

  這種時候直接禁止選取超過一年內的日期,可以避免使用者不小心進行錯誤的操作。


實作案例

  要設定 <input type=”date”> 的範圍其實用原生的 JS 就能做到,只需要增加 max 或 min 的屬性即可:

<html>
<head>
<title>Date Range Example</title>
</head>
<body>
<input type="date" id="dateInput">

<script>
// 獲取今日日期
const today = new Date();
// 設定最大值為今日
const maxDate = today.toISOString().split("T")[0];
// 設定最小值為一年前
today.setFullYear(today.getFullYear() - 1);
const minDate = today.toISOString().split("T")[0];

// 設置 input 的最小值和最大值
document.getElementById('dateInput').max = maxDate;
document.getElementById('dateInput').min = minDate;
</script>
</body>
</html>


  而在 Vue 中就可以這樣寫:

<template>
<input type="date" :max="maxDate" :min="minDate">
</template>

<script setup>
import { ref, onMounted } from 'vue';

const today = new Date;
const maxDate = ref('');
const minDate = ref('');

function setDateRange() {
maxDate.value = today.toISOString().split("T")[0];
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().split("T")[0];
}

onMounted(() => {
setDateRange();
})


</script>


語法解䆁

input date 屬性:max 與 min

  max 與 min 都是 <input type=”date”> 本身就擁有的屬性,從 MDN 中的說明可以看到,設定的時候需符合 yyyy-mm-dd 這樣的日期格式。因此如果有一個已知的固定時間,也是可以直接把 max 或 min 的值寫死。


new Date():動態取得今日日期

  因為在這個案例中,要取得的日期範圍是「從今日開始到一年前」這個動態的區間,因此可以用new Date() 這個方法來取得現在日期和時間的 Date 物件。

  console.log() 之後看到的內容會是:Tue Oct 22 2024 00:00:00 GMT+0800 (台北標準時間) 這樣的格式,而我們需要讓它轉成 2024-10-22 才能被我們的 max 給接受,因此需要進行下一步:


.toISOString():把日期格式轉成 YYYY-MM-DDTHH:mm:ss.sssZ

  toISOString() 是 Date 物件的一個內建方法,他會返回一個 ISO 8601(國際標準化組織的日期和時間的表示方法) 格式的字符串,但要注意一個問題是它返回的值會是 UTC 時區的值,也就是以台灣 UTC+8 來說,它的 HH 會比我們使用當下要少 8 小時,假設我們使用的時間在早上八點以前,那麼得到的 max 日期就不會是當天,而是前一天。

  如果想避免這個情況發生,也可以用函式手動將 Date 物件進行格式化,格式化的函式放到最後再來說明。


.split(“T”)[0]:提取 YYYY-MM-DD

  前面已經透過 .toISOString() 把 Date 物件轉成 YYYY-MM-DDTHH:mm:ss.sssZ 格式了,但我們需要的部份其實只有 YYYY-MM-DD 這部份而已,因此用字串內建的方法 split(),以T這個字為切割點,把整個字串切成了陣列:[ ‘YYYY-MM-DD‘, ‘HH:mm:ss.sssZ‘ ],而其中第 0 項的 ‘YYYY-MM-DD’ 就是我們需要的部份。

  其實用另一個字串的內建方法 slice() 也能達到一樣的效果,因為 YYYY-MM-DDTHH:mm:ss.sssZ 的格式是固定的,我們需要的 YYYY-MM-DD 區塊必定是字串的前十個字,因此用 .slice(0,10) 也可以獲得我們需要的部份,寫法會像這樣:

function setDateRange() {
maxDate.value = today.toISOString().slice(0,10);
today.setFullYear(today.getFullYear() - 1);
minDate.value = today.toISOString().slice(0,10);
}


如何手動格式化 Date 物件

  為了避免時差導致日期錯誤,我們可以設計一個函式來將 Date 物件手動格式化成 YYYY-MM-DD 的格式,具體寫法如下:

const today = new Date()

function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}

formatDate(today);


  這個函式的思路很簡單,就是分別把日期物件的年、月、日提取出來之後,再重新組合成我們要的格式。其中值得注意的部份有兩點:

  1. 用 getMonth() 得到的值是 monthIndex,因此要再 +1 才會是正確的月份。
  2. 用 getFullYear()、getMonth()、getDate() 取得的值都是數字,如果月或日是個位數的話需要補零,為了補零就需要先把數字轉成字串。轉成字串之後再用 padStart() 來確保字符串至少有兩個字符,若不足兩個字符的時候就在前面補零。


  套用到案例中的話,就會是這樣:

<template>
<input type="date" :max="maxDate" :min="minDate">
</template>

<script setup>
import { ref, onMounted } from 'vue';

const today = new Date;
const maxDate = ref('');
const minDate = ref('');

function setDateRange() {
maxDate.value = formatDate(today);
today.setFullYear(today.getFullYear() - 1);
minDate.value = formatDate(today);
}

function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}

onMounted(() => {
setDateRange();
})


</script>


參考資料

MDN - <input type=”date”>

MDN - Date

JavaScript 之旅 (5):String method - padStart & padEnd

AI 工具 - Microsoft Copilot



分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.