Element Form表單實踐(下)
阿新 • • 發佈:2020-05-28
![](https://img2020.cnblogs.com/blog/774496/202005/774496-20200528104334717-1211753291.png)
>作者:小土豆biubiubiu
>
>部落格園:https://www.cnblogs.com/HouJiao/
>
>掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
>
>
>微信公眾號:土豆媽的碎碎念(掃碼關注,一起吸貓,一起聽故事,一起學習前端技術)
>
>碼字不易,點贊鼓勵喲~
# 前言
上一篇文章 [Element Form表單實踐(上)](https://www.cnblogs.com/HouJiao/p/12924324.html#4583766)參照著文件將表單部分內容實踐了一下。
這篇文章將分享專案開發中的一個表單實踐,最終做出來的效果大致是下面這個樣子:
![](https://user-gold-cdn.xitu.io/2020/5/20/17231918a2719054?w=1012&h=701&f=gif&s=578644)
這個表單看似是比較簡單的,但實際上比一般表單存在一些細節的東西需要設計和處理。
接下來就來完成這個功能。
# 主頁面
首先是`主頁面`的實現。
![](https://user-gold-cdn.xitu.io/2020/5/20/17231a1127c522f1?w=689&h=217&f=png&s=3546)
`主頁面`的邏輯非常簡單,直接將程式碼貼出來。
```html
Element Form表單實踐
詳細配置
儲存
```
這段程式碼中的內容都是上一篇文章中實踐過的,沒有什麼特別需要說明的點。
# 詳細配置頁面
![](https://user-gold-cdn.xitu.io/2020/5/26/1725016f95e2235d?w=623&h=547&f=png&s=7521)
### 簡單實現
詳細配置頁面實際上也是一個表單,我們先來把介面中需要展示的元件畫出來。
```html
Element Form表單實踐
詳細配置
儲存
個人資訊
住址資訊
儲存
重置
```
上面這段程式碼主要添加了兩個邏輯:`彈窗元件`和彈窗內部的`表單元件`。
### 彈窗元件
彈窗元件使用的是`element`的`dialog`來實現。
主要的邏輯包含定義彈窗是否顯示的`data`資料`modelVisible`、點選`詳細配置`設定彈框可見以及彈窗元件的使用。
定義彈窗是否顯示的`data`資料`modelVisible`:
```javascript
var vm = new Vue({
data: {
// 彈窗是否顯示
modelVisible: false
}
})
```
點選`詳細配置`設定彈框可見:
```html
詳細配置
```
彈框元件使用:
```html
```
彈窗元件的實現和使用非常簡單,沒有特別需要說明的點。
最後在看一下效果。
![](https://user-gold-cdn.xitu.io/2020/5/21/172368ac6d6e67df?w=983&h=649&f=gif&s=110128)
### 表單元件
表單元件的程式碼如下:
```html
個人資訊
住址資訊
儲存
重置
```
可以看到,表單元件的程式碼非常的簡單,前一篇文章中實踐的內容這裡都還沒有新增。
那接下來結合本節需要實現的這個功能新增上一節中實踐過的內容。
# 表單新增model屬性
首先第一個最重要的就是表單的`model`屬性,也就是表單繫結的資料。
這裡我們先定義一個簡單的`表單資料`。
```javascript
detailConfigForm: {
personalInfo: false, // 個人資訊
age:'', // 年齡
height: '', // 身高
addressInfo: true, // 住址資訊
province: '', // 省份
city: '' // 城市
}
```
然後將該資料繫結到表單上,同時為`表單項(el-form-item)`新增`model`、`prop`屬性。
```html
個人資訊
住址資訊
儲存
重置
```
完成後,此時在表單中填寫內容已經沒有問題了。
![](https://user-gold-cdn.xitu.io/2020/5/25/1724b638d69d9f2d?w=611&h=537&f=gif&s=209395)
# 個人資訊/住址資訊啟用禁用
接著需要實現的功能是:將`個人資訊/住址資訊`當做一個`開關`,`選中`時對應模組的控制元件`啟動`,可以正常填寫內容;`取消選中`時,對應的模組控制元件`禁用`,且`清空`上一次填寫的內容。
![](https://user-gold-cdn.xitu.io/2020/5/25/1724b732d7bacffc?w=611&h=541&f=gif&s=197826)
那我們知道表單項設定`disabled`值就可以實現。
根據前面描述`禁用`和`啟用`的邏輯,可以發現`個人資訊/住址資訊`的`啟用`、`禁用`跟`表單`的`禁用`、`啟用`剛好是`相反`的邏輯。
所以目前實現的思路就是:將`detailConfigForm.personalInfo`的值`取反`繫結在`年齡`、`身高`控制元件的`disabled`屬性上;將`detailConfigForm.addressInfo`的值`取反`繫結在`省份`、`城市`控制元件的`disabled`屬性上。
> 這裡我們只將`個人資訊`部分的邏輯實現程式碼貼出來
```html
```
在來看下效果。
![](https://user-gold-cdn.xitu.io/2020/5/26/1724eb10a645a21c?w=611&h=265&f=gif&s=75639)
> `地址資訊`這部分的`禁用`、`啟用`邏輯和`個人資訊`是相同的,這裡不在多說
# 取消啟用清空對應控制元件中填寫的內容
那接下來要實現的功能就是`取消啟用清空對應控制元件中填寫的內容`。
![](https://user-gold-cdn.xitu.io/2020/5/26/1724ec3d24e1216e?w=611&h=265&f=gif&s=23275)
### 方式一:手動賦空值
上一節的 [Element Form表單實踐(上)](https://juejin.im/post/5ea79710e51d454dd506176b) 中說過表單的`resetFileds`方法可以重置表單。
```javascript
this.refs['formName'].resetFields()
```
不過該方法會重置表單中的所有屬性,所以說不太符合我們的要求。我們只需要重置部分表單:即`個人資訊`取消啟用時,只需要清空`年齡`、`身高`這兩個內容即可。
解決這個問題的思路之一就是放棄使用`resetFields`方法,直接給表單資料`賦空值`從而`清空表單內容`。
`清空表單內容`這個操作是在`個人資訊`啟用和禁用的時候執行的,即在`detailConfigForm.personalInfo`值發生變化時執行的,那這個很自然的就會想到使用`vue watch` 屬性監聽`detailConfigForm.personalInfo`的變化,在該值為`false`的時候,給表單資料賦值為空,實現`清空表單內容`。
```javascript
watch: {
'detailConfigForm.personalInfo': function(val){
if(val == false){
this.detailConfigForm.age = "";
this.detailConfigForm.height = "";
}
}
},
```
![](https://user-gold-cdn.xitu.io/2020/5/26/1724ed5bf442b233?w=611&h=265&f=gif&s=105865)
然而在真正的專案實踐中,當取消啟用`個人資訊`時,需要清空的表單數量不止兩個,而是有多個,所以作者就放棄了這種手動賦值清空的方式。
> 放棄這種方式的原因還有一個,就是表單的驗證也會存在問題。
>
> 表單填寫完成後,點選提交,假如個人資訊沒有啟用,那驗證時就不需要對年齡和身高進行驗證,而表單的驗證方法`validate`是對整個表單進行校驗的方法。
>
> 這幾個因素是我放棄手動賦值清空方式的重要原因。
### 方式二:resetFileds
放棄手動賦值清空表單的這種方式後,我又迴歸到了表單的`resetFields`方法。既然還想使用`resetFields`方法,唯一的辦法就是做一個`表單巢狀`。
![](https://user-gold-cdn.xitu.io/2020/5/26/1724ee3bcaeeeafe?w=468&h=420&f=png&s=12666)
這樣當`個人資訊`取消啟用時,就可以呼叫`this.refs['personalInfoForm'].resetFields()`重置個人資訊這部分的表單內容。而在整個表單提交驗證的時候,也可以分別呼叫`this.refs['personalInfoForm'].validate()`和`this.refs['addressInfoForm'].validate()`分開進行驗證。
##### 修改資料結構
那這種實現思路的第一步就是將表單的資料結構進行修改。
```javascript
detailConfig: {
personalInfoConfig:{
personalInfo: false, // 個人資訊
age: '', // 個人資訊-年齡
height: '', // 個人資訊-年齡
},
addressInfoConfig:{
addressInfo: false, // 地址資訊
province: '', // 地址資訊-省份
city: '' // 地址資訊-城市
}
}
```
##### 重寫表單程式碼
接下來就需要根據這樣的資料結構將`el-form`表單的程式碼進行重寫。
```html
個人資訊
住址資訊
儲存
重置
```
##### 使用resetFileds
接著在修改一下`watch`程式碼。
```javascript
watch:{
"detailConfig.personalInfoConfig.personalInfo": function(personalInfo){
if(personalInfo == false){
this.$refs['personalInfoForm'].resetFields()
}
},
"detailConfig.addressInfoConfig.addressInfo": function(addressInfo){
if(addressInfo == false){
this.$refs['addressInfoForm'].resetFields()
}
}
},
```
可以看到`watch`程式碼內部就可以直接使用表單的`resetFields`方法,對`個人資訊`和`住址資訊`分開進行清空。
最終的結果和手動賦值清空是一樣的,這裡不在演示。
# 表單驗證
最後一個就是表單的驗證了。
### rules
首先我們需要編寫表單的`rules`驗證規則。
```javascript
detailConfig: {
personalInfoConfig:{
personalInfo: false,
age: '',
height: '',
rules: {
age: [{
type: 'number',
message: '年齡必須為數字值'
}],
height: [{
type: 'number',
message: '身高必須為數字值'
}]
}
},
addressInfoConfig:{
addressInfo: false,
province: '',
city: '',
rules: {
province: [
{ min: 2, max: 10, message: '長度必須在2-10個字元'}
],
city: [
{ min: 2, max: 10, message: '長度必須在2-10個字元' }
]
}
}
}
```
新增的驗證規則如下:
年齡和身高:必須為數值;
省份和城市:長度必須在2-10個字元。
### 使用validate
接著在儲存按鈕的`click`事件上繫結`saveConfig`方法。
```html
儲存
```
接著編寫`saveConfig`的邏輯。
> 需要說明的是,只有對應的按鈕啟用了,才會對對應啟用的表單做驗證
```javascript
saveConfig(){
// 如果個人資訊啟用,則需要對個人資訊下的年齡、身高欄位進行驗證。
if(this.detailConfig.personalInfoConfig.personalInfo){
this.$refs['personalInfoForm'].validate((valid,failedInfo) => {
// 個人資訊下的年齡、身高欄位進行驗證通過。
if(valid){
// 判斷地址資訊是否啟用,啟用的話需要對地址資訊下的城市、省份進行驗證
if(this.detailConfig.personalInfoConfig.personalInfo){
this.$refs['addressInfoForm'].validate((valid,failedInfo) => {
// 地址資訊下的城市、省份驗證成功。關閉dialog
if(valid){
this.modelVisible = false;
}else{
return false;
}
})
}else{
this.modelVisible = false;
}
}else{
return false;
}
})
// 如果地址資訊啟用,則需要對地址資訊下的省份、城市欄位進行驗證。
}else if(this.detailConfig.addressInfoConfig.addressInfo){
this.$refs['addressInfoForm'].validate((valid,failedInfo) => {
if(valid){
this.modelVisible = false;
}else{
return false;
}
})
// 個人資訊和地址資訊均沒有啟用,直接關閉dialog
}else{
this.modelVisible = false;
}
},
```
> 這部分的邏輯比較繁瑣,因為存在`啟用驗證`、`不啟用就不驗證`的邏輯判斷
完成後,最終的效果我們再來看一下。
![](https://user-gold-cdn.xitu.io/2020/5/27/17254122cc654051?w=967&h=691&f=gif&s=791571)
# 重置表單
首先在頁面上新增重置按鈕,繫結事件。
```html
重置
```
接著就來使用表單的重置方法`resetFileds`來重置表單的內容。
那這裡需要注意的一點就是我們的表單是巢狀表單。
![](https://user-gold-cdn.xitu.io/2020/5/27/17254966448bd5a7?w=468&h=420&f=png&s=12453)
直接呼叫`外層表單`的`resetFileds`方法沒有辦法去重置表單內容。因此這裡必須呼叫內層表單的`resetFileds`方法。
```javascript
resetForm(formName){
this.$refs['personalInfoForm'].resetFields();
this.$refs['addressInfoForm'].resetFields();
// 呼叫外層表單的`resetFileds`方法沒有辦法去重置表單內容
// this.$refs['detailForm'].resetFields();
}
```
> 表單重置這裡就不貼演示結果了
# 功能優化和bug修復
到這裡我們表單的大部分功能已經實現了:`表單禁用啟用`、`表單禁用時清空表單內容`、`表單驗證`、`表單重置`。
那接下來就需要對實現的這個功能進行在思考。
### 功能優化
第一個是功能優化。
回頭看所有實現的功能,唯一覺得不太合適的地方就是表單的驗證邏輯。
```javascript
saveConfig(){
// 如果個人資訊啟用,則需要對個人資訊下的年齡、身高欄位進行驗證。
if(this.detailConfig.personalInfoConfig.personalInfo){
this.$refs['personalInfoForm'].validate((valid,failedInfo) => {
// 個人資訊下的年齡、身高欄位進行驗證通過。
if(valid){
// 判斷地址資訊是否啟用,啟用的話需要對地址資訊下的城市、省份進行驗證
if(this.detailConfig.addressInfoConfig.addressInfo){
this.$refs['addressInfoForm'].validate((valid,failedInfo) => {
// 地址資訊下的城市、省份驗證成功。關閉dialog
if(valid){
this.modelVisible = false;
}else{
return false;
}
})
}else{
this.modelVisible = false;
}
}else{
return false;
}
})
// 如果地址資訊啟用,則需要對地址資訊下的省份、城市欄位進行驗證。
}else if(this.detailConfig.addressInfoConfig.addressInfo){
this.$refs['addressInfoForm'].validate((valid,failedInfo) => {
if(valid){
this.modelVisible = false;
}else{
return false;
}
})
// 個人資訊和地址資訊均沒有啟用,直接關閉dialog
}else{
this.modelVisible = false;
}
}
```
可以看到這裡有多層巢狀的邏輯判斷。
那這個驗證功能無非就是希望當前不選中那一項就不驗證那一項,那能不能將校驗規則定義為動態的,不選中時`移除`校驗規則,選中時`新增`上校驗規則。
那麼答案是可以的,所以接下來就來實現一下。
首先我們在`data`資料中定義多個規則。
```javascript
personalInfoConfig:{
personalInfo: false,
age: '',
height: '',
rules: {
age: [{
type: 'number',
message: '年齡必須為數字值'
}],
height: [{
type: 'number',
message: '身高必須為數字值'
}]
},
// 定義空的驗證規則
emptyRules: {}
},
addressInfoConfig:{
addressInfo: false,
province: '',
city: '',
rules: {
province: [
{ min: 2, max: 10, message: '長度必須在2-10個字元'}
],
city: [
{ min: 2, max: 10, message: '長度必須在2-10個字元' }
]
},
// 定義空的驗證規則
emptyRules: {}
}
```
> 即一個正常的驗證規則,對應複選框啟用時的驗證;還要一個空的驗證規則,對應複選框取消啟用時的驗證。
然後我們將規則定義到`計算屬性`中。
```javascript
computed:{
personalInfoRules: function(){
if(this.detailConfig.personalInfoConfig.personalInfo == true){
return this.detailConfig.personalInfoConfig.rules;
}else{
return this.detailConfig.personalInfoConfig.emptyRules;
}
},
addressInfoRules: function(){
if(this.detailConfig.addressInfoConfig.addressInfo == true){
return this.detailConfig.addressInfoConfig.rules;
}else{
return this.detailConfig.addressInfoConfig.emptyRules;
}
}
},
```
接著就是將`計算屬性`繫結到對應表單的`rules`屬性上。
```html
```
可以看到`el-form`上繫結的`rules`已經修改為`computed`中定義的屬性了。
這樣的改動完成之後,最後一步就是重寫校驗邏輯了。
```javascript
saveConfig(){
this.$refs['personalInfoForm'].validate((valid,failedInfo) => {
// 個人資訊下的年齡、身高欄位進行驗證通過。
if(valid){
this.$refs['addressInfoForm'].validate((valid,failedInfo) => {
// 地址資訊下的城市、省份驗證成功,關閉dialog
if(valid){
this.modelVisible = false;
}else{
return false;
}
})
}else{
return false;
}
})
},
```
因為規則在動態的變化,而驗證的邏輯就不需要複選框的啟用禁用進行判斷,直接使用規則進行驗證即可。所以的驗證邏輯是不是就清爽了很多。
> 那這個就是針對表單驗證做的一個小小的優化。
> 如果大家有更好的方法可以分享給我
### bug修復
在功能測試的過程中,我還發現一個問題。
當我在表單中填寫了錯誤格式的資料後,直接通過點選彈窗上方的叉號按鈕來關閉`dialog`(不點選儲存按鈕),那此時`detailConfig`中的欄位值已經是那個錯誤格式的資料(雙向資料繫結原理),如果直接將最終的`detailConfig`傳送到後端顯然是不對的。
目前暫時還沒有一個好的解決思路,正在思考中,歡迎大家和我交流。
# 完整程式碼
最後我將本次實踐的完整程式碼貼在這裡。
```html
Element Form表單實踐
詳細配置
儲存
個人資訊
住址資訊
儲存
重置
```
> 注意在主頁面儲存整個表單內容是,我使用ES6的`展開運算子`將主頁面的表單資料和彈框元件內的表單資料合併到了一起,這樣就可以直接將合併後的資料傳送到後端。
> 使用展開運算符合並資料雖然比較方便,但是定義的驗證規則資料也會包含在最終的結果中。
# 寫在最後
作者實現的這個功能是在一個本來完整的表單提交功能上新增的一個小功能。當時已經完成的表單的資料結構是根據業務和邏輯設計的多層巢狀字典,所以後面我`新增`的這個`資料結構`也是巢狀在字典裡層的。
```javascript
// 這個資料是根據業務邏輯設計的多層巢狀字典
people{
// 這裡還有別的表單的資料
// config是我新增的表單資料
config:{
icmpconfig:{
time:10
},
tcpconfig:{
time:10
}
}
}
```
但是剛一開始我並沒有做表單巢狀,而是在外層使用單個的`el-form`實現。
到後面做驗證新增`rules`的時候,`prop`的值就得寫成`people.icmpconfig.time`,但實際是`prop`是不能寫成這樣`.`的形式,寫了之後會報錯說`time`沒有定義。
介於這個原因,在綜合前面的說法:
![](https://user-gold-cdn.xitu.io/2020/5/27/17255c3f612af966?w=610&h=200&f=png&s=16004)
是一整個使用表單巢狀的原因。
使用表單巢狀感覺有利也有弊,方便了一些邏輯,也帶來了一些問題。
所以一定要提前設計好,選擇一個合理的實現方式。
# 關於
### 作者
小土豆biubiubiu
> 一個努力學習的前端小菜鳥,知識是無限的。堅信只要不停下學習的腳步,總能到達自己期望的地方
>
> 同時還是一個喜歡小貓咪的人,家裡有一隻美短小母貓,名叫土豆
### 部落格園
https://www.cnblogs.com/HouJiao/
### 掘金
https://juejin.im/user/58c61b4361ff4b005d9e894d
### 微信公眾號
土豆媽的碎碎念
> 微信公眾號的初衷是記錄自己和身邊的一些故事,同時會不定期更新一些技術文章
>
> 歡迎大家掃碼關注,一起吸貓,一起聽故事,一起學習前端技術
### 作者寄語
小小總結,歡迎大