提升網頁表單驗證:從簡單檢查到全面優化

在網頁開發中,表單驗證是一個常見卻極為重要的任務。今天,我想和大家分享我最近在優化一個複雜表單驗證過程中的經驗和心得。

初始問題:日期驗證邏輯

一開始表單只是單純使用HTML中date的max跟min屬性來限制,結果使用者通報在蘋果手機的瀏覽器無法正常運作,經檢查發現該屬性不支援,所以有了在表單中添加日期驗證需求:

為了解決這個問題,整理了以下驗證需求:

  1. 計算最小允許的開始日期(現在日期加7天)。
  2. 檢查用戶輸入的開始日期是否符合要求。
  3. 如果日期無效,自動將其修正為最早允許的日期。

代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const today = new Date();
const minStartDate = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);

// 檢查開始日期
if (this.business.editdata.startDate) {
const startDate = new Date(this.business.editdata.startDate);
if (startDate < minStartDate) {
alert('開始日期必須是現在日期加7天或之後');
// 自動修正為最早允許的日期
this.business.editdata.startDate = minStartDate.toISOString().split('T')[0];
isValid = false;
}
} else {
alert('拍攝開始日期為必填項目');
// 自動設置為最早允許的日期
this.business.editdata.startDate = minStartDate.toISOString().split('T')[0];
isValid = false;
}

// 檢查結束日期
if (this.business.editdata.endDate) {
const endDate = new Date(this.business.editdata.endDate);
const startDate = new Date(this.business.editdata.startDate);
if (endDate < startDate) {
alert('結束日期不得早於開始日期');
// 自動修正為開始日期
this.business.editdata.endDate = this.business.editdata.startDate;
isValid = false;
}
} else {
alert('拍攝結束日期為必填項目');
// 自動設置為開始日期
this.business.editdata.endDate = this.business.editdata.startDate;
isValid = false;
}

全面優化表單驗證

在解決了日期驗證的問題後,我們意識到整個表單驗證過程都可以進行優化。以下是我們全面優化後的 validateForm 函數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
validateForm() {
let isValid = true;
const today = new Date();
const minStartDate = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);

const requiredFields = [
{ field: 'company', message: '公司名稱為必填項目' },
{ field: 'GUInumber', message: '公司統編為必填項目' },
{ field: 'representative', message: '法定代表人為必填項目' },
{ field: 'address', message: '公司地址為必填項目' },
{ field: 'applicant', message: '申請人姓名為必填項目' },
{ field: 'contact', message: '現場聯絡人為必填項目' }
];

// 檢查必填欄位
requiredFields.forEach(({ field, message }) => {
if (!this.business.editdata[field]) {
alert(message);
isValid = false;
}
});

// 檢查 Email 格式
if (!this.business.editdata.aMail || !/\S+@\S+\.\S+/.test(this.business.editdata.aMail)) {
alert('申請人Email為必填項目,並且需為有效的Email格式');
isValid = false;
}

// 檢查手機號碼格式
const phoneFields = [
{ field: 'aPhone', message: '申請人手機為必填項目,並且需為有效的手機號碼格式' },
{ field: 'cPhone', message: '現場聯絡人手機為必填項目,並且需為有效的手機號碼格式' }
];

phoneFields.forEach(({ field, message }) => {
if (!this.business.editdata[field] || !/^\d{10}$/.test(this.business.editdata[field])) {
alert(message);
isValid = false;
}
});

// 檢查開始日期和結束日期(使用前面優化過的邏輯)

// 檢查拍攝資訊
if (this.mapdata.length < 1) {
alert("請填寫「拍攝資訊」");
isValid = false;
}

// 檢查工作人員名冊
const hasStaffMember = this.business.dbStaffMember.some(staff => !staff.remove);
if (this.business.editdata.staff.length < 1 && !hasStaffMember) {
alert("請填寫「拍攝工作人員名冊」");
isValid = false;
}

// 檢查企劃案文件
const hasPlanningFile = this.business.dbFileSet.some(file => file.category === 'planning' && !file.remove);
if (!hasPlanningFile && this.business.editdata.file_planning.length < 1) {
alert("請上傳「企劃案(腳本)或拍攝計畫書」");
isValid = false;
}

return isValid;
}

優化重點

  1. 模組化驗證邏輯:將相似的驗證邏輯(如必填欄位檢查)組織成可重用的結構。
  2. 智能日期處理:不僅檢查日期有效性,還自動修正無效輸入。
  3. 全面性檢查:除了基本的欄位驗證,還包括了特殊需求(如檢查拍攝資訊、工作人員名冊等)。
  4. 代碼可讀性:使用描述性變量名稱和註釋,使代碼易於理解和維護。

未來改進方向

雖然我們的表單驗證已經比之前強大許多,但仍有改進空間:

  1. 錯誤訊息處理:目前使用 alert 來顯示錯誤,可以考慮使用更友善的 UI 元素。
  2. 非同步驗證:對於需要與後端確認的欄位(如公司統編),可以實現非同步驗證。
  3. 即時驗證:考慮在用戶輸入時就進行驗證,而不是等到表單提交時。
  4. 國際化:如果網站需要支援多語言,可以將錯誤訊息抽離出來,便於翻譯。

結論

優化表單驗證是一個持續的過程。通過這次優化,我們不僅解決了特定的日期驗證問題,還全面提升了整個表單的用戶體驗。記住,好的表單驗證應該是即時的、清晰的,並且能夠優雅地處理各種情況。