背景
當我們在做後臺管理系統時,經常會遇到非常複雜的表單:
- 表單項非常多
- 在各種表單型別下,顯示不同的表單項
- 在某些條件下,某些表單項會關閉驗證
- 每個表單項還會有其他自定義邏輯,比如輸入框可以插入模板變數、輸入字元數量顯示、圖片上傳並顯示、富文字。。。
- 在這種錯綜複雜的情況下,完成表單的驗證和提交
- 可以檢視具體例子:例子中省略了很多瑣碎的功能,只保留整體的複雜表單框架,用於展示解決方案
方案1: 在一個vue
檔案中
所有的表單項顯示隱藏、驗證、資料獲取、提交、自定義等邏輯放在一起
- 根據表單型別,使用
v-if/v-show
處理表單項顯示隱藏- 在
elementui
自定義驗證中,根據表單型別,判斷表單項是否驗證- 根據表單型別,獲取不同的資料,並提交到不同的介面
- 其餘所有的自定義邏輯
缺點
- 亂
- 亂
- 還是亂
- 一個
vue
檔案,輕輕鬆鬆上2000
行 - 在我嘗試加入一種新的表單型別時,我發現我已經無。從。下。手。
方案2:分離元件
其實很容易想到根據不同的表單型別,分離出多個相應型別的子表單。但我在實踐時還是遇到了很多問題:父子表單驗證、整體提交資料的獲取等等,並總結出一套解決方案:
1. 子元件
所有的子元件中都需要包含兩個方法validate
、getData
供父元件呼叫。
(1) validate
方法
用於驗證本身元件的表單項,並返回一個promise
物件
vaildate() {
// 返回`elementUI`表單驗證的結果(為`promise`物件)
return this.$refs["ruleForm"].validate();
},
複製程式碼
(2) getData
方法
提供子元件中的資料
getData() {
// 返回子元件的form
return this.ruleForm;
},
複製程式碼
2. 父元件
(1) 策略模式
使用策略模式儲存並獲取子表單的ref
(用於獲取子表單的方法)和提交函式 。省略了大量的if-else
判斷。
data:{
// type和ref名稱的對映
typeRefMap: {
1: "message",
2: "mail",
3: "apppush"
},
// type和提交函式的對映。不同型別,介面可能不同
typeSubmitMap: {
1: data => alert(`簡訊模板建立成功${JSON.stringify(data)}`),
2: data => alert(`郵件模板建立成功${JSON.stringify(data)}`),
3: data => alert(`push模板建立成功${JSON.stringify(data)}`)
},
}
複製程式碼
(2) submit
方法
用於父子元件表單驗證、獲取整體資料、呼叫當前型別提交函式提交資料
因為
elementUI
表單驗證的validate
方法可以返回promise
結果,可以利用promise
的特性來處理父子表單的驗證。 比如then
函式可以返回另一個promise
物件、catch
可以獲取它以上所有then
的reject
、Promise.all
。
- 父表單驗證通過才會驗證子表單,存在先後順序
// 父表單驗證通過才會驗證子表單,存在先後順序 submitForm() { const templateType = this.typeRefMap[this.indexForm.type]; this.$refs["indexForm"] .validate() .then(res => { // 父表單驗證成功後,驗證子表單 return this.$refs[templateType].vaildate(); }) .then(res => { // 全部驗證通過 // 獲取整體資料 const reqData = { // 獲取子元件資料 ...this.$refs[templateType].getData(), ...this.indexForm }; // 獲取當前表單型別的提交函式,並提交 this.typeSubmitMap[this.indexForm.type](reqData); }) .catch(err => { console.log(err); }); }, 複製程式碼
- 父表單,子表單一起驗證
submitForm1() { const templateType = this.typeRefMap[this.indexForm.type]; const validate1 = this.$refs["indexForm"].validate(); const validate2 = this.$refs[templateType].vaildate(); // 父子表單一起驗證 Promise.all([validate1, validate2]) .then(res => { // 都通過時,傳送請求 const reqData = { ...this.$refs[templateType].getData(), ...this.indexForm }; this.typeSubmitMap[this.indexForm.type](reqData); }) .catch(err => { console.log(err); }); }, 複製程式碼
檢視線上專案、專案github和元件程式碼
總結:很多專案我都遇到這種複雜的表單,也用了很多種解決方案,在此總結出了一種比較整潔簡便的方案。當然還有其他很多方案,比如可以把資料提交的方法放在每一個子元件中,公共的表單項資料通過
props
傳遞給子元件用於提交。有其他更加簡潔的方案,歡迎評論,或者github
上提issue
題外話: 看了前端架構師親述:前端工程師成長之路的 N 問 及 回答中的幾個回答後,對我有很大的啟發。在對自己的技術方向、前景迷茫時、或者在埋怨自己的專案太low時、或者埋怨自己每天在做重複工作時、或者每天對層出不窮的新技術焦頭爛額時,不妨認真的審視下自己的專案,
- 每天重複的工作,是不是可以自己造輪子了;
- 技術棧太low,是不是可以平滑過渡到新技術,提高開發效率;
- 學再多的新技術,最終也會迴歸並實踐到專案中。
從工作流程和專案的痛點出發,你會在實踐、總結並解決實際問題中進步的更加迅速。
寫這篇文章的感受:把這些東西表達出來的難度
>>
文章本身所包含的技術難度