在 Vue 中多表單深層次嵌套結構,創建響應式屬性,以及多層級表單驗證
設置響應式屬性
在 Vue 中,只有在跟級別中創建的屬性,才具有響應式,如果後期手動添加,如使用 this.xxx = xxx
這種方式創建的屬性是不具有響應式的。
在 Vue裏面,封裝了 7 個方法,使用如下的方法來添加屬性,同樣可以創建響應式屬性,並觸發視圖更新:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
如果不使用以上的方法,Vue 也提供了$set
方法來設置響應式屬性,使用方式爲this.$set(obj, newObj, value)
,在 Vue 的單文件組件裏,大概是這樣寫的:
<script>
export default {
data() {
return {
obj: {}
}
},
methods: {
setValue() {
this.$set(this.obj, 'name', 'tim')
}
}
}
</script>
以上的代碼表示,在 obj
裏面,添加一個名爲 name
值爲 value
的屬性。添加完之後,data 裏面應該是這樣的:
data() {
return {
obj: {
name: 'tim'
}
}
}
爲了驗證一下有效性,我們新建一個組件,ui 框架選用 element,來測試一下到底是否可行:
<template>
<div class="test">
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
</el-form>
<el-button type="primary" @click="testFun">test</el-button>
</div>
</template>
<script>
export default {
name: 'test',
data() {
return {
form: {
}
}
},
methods: {
testFun() {
this.form.name = 'tim'
}
}
}
</script>
<style scoped></style>
先點擊 test
按鈕,我們往 form 裏面添加了 name 屬性,然後在表單輸入值,發現輸入不進去,打開 devtools
發現值也沒有改變。
我們把 testFun
函數改寫一下:
testFun() {
this.$set(this.form, 'name' 'tim')
}
這回在測試一下,先點擊 test
按鈕,然後發現表單被賦上值了,然後修改表單同樣能夠更新值。
以上表示,如果我們沒有一開始在 data 裏面創建跟級別的屬性,需要使用 $set
來創建。
不過我們要說的不止是這個,而是創建多層嵌套的二維三維數組和對象的響應式,並觸發視圖更新。實際應用主要在表單的多層嵌套上,需要復原數據的情況下。
現在假設我們需要復原的數據爲如下格式:
let copyData = [
[
{
name: 'tim',
age: 10
},
{
name: 'tim',
age: 20
}
],
[
{
name: 'andy',
age: 15
},
{
name: 'andy',
age: 30
},
]
]
這裏面,既有數組又有對象,基本能把創建多維數據的響應式屬性講清楚。對於創建響應式數組和對象的原理,可以點擊這裏查看官網的介紹(建議看一眼之後,再來看這個例子),在此只提供實例進行講解。
- 首先,我們先創建一個初始化頁面,一般來說,我們不可能創建一個空的表單 data,所以需要填入屬性,只不過沒有值而已。
<template>
<div class="test">
<el-form ref="form" :model="form" label-width="80px">
<div class="parent" v-for="(parent, index) in form.list" :key="`parent-${index}`">
<div class="child" v-for="(child, idx) in parent" :key="`child-${idx}`">
<el-form-item label="name">
<el-input v-model="child.name"></el-input>
</el-form-item>
<el-form-item label="age">
<el-input v-model="child.age"></el-input>
</el-form-item>
</div>
</div>
</el-form>
<el-button type="primary" @click="testFun">test</el-button>
</div>
</template>
<script>
export default {
name: "test",
data() {
return {
form: {
list: [
[
{
name: "",
age: ""
}
]
]
}
};
},
};
</script>
<style scoped></style>
以上是完整的一個單 Vue 文件。主要是初始化了一個嵌套的多層級表單。
如上,我們在表單裏修改內容,在 devtools 裏面,可以看到值能夠更新
2. 有了以上的佈局結構,我們就可以模擬數據了。
let copyData = {
list: [
[
{
name: "tim",
age: 10
},
{
name: "tim",
age: 20
}
],
[
{
name: "andy",
age: 15
},
{
name: "andy",
age: 30
}
]
]
};
假設我們通過接口獲取到如上數據,然後我們需要把如上的數據寫入到頁面裏面,那麼我們直接通過 this.form = copyData
只有第一層級的基礎數據結構有效果(Boolean、String、Number),其他是沒有效果的(Array,Object),也就是說,在多層級嵌套的情況下,我們需要通過 Vue 提供的上述 7 種方法,或者 $set
來設置響應式屬性。
-
這次我們使用
$set
來完成響應式屬性的設置。首先我們看
copyData
裏面的list
,是一個數組,所以我們需要使用$set
來完成對數組的操作。你也可以使用 vm.set(vm.items, indexOfItem, newValue)
上面的
vm.items
其實就是要設置的對象,indexOfItem
我們可以通過遍歷list
來獲取index
,newValue
就是對應的值。copyData.list.forEach((parent, index) => { this.$set(this.form.list, index, copyData.list[index]); });
通過以上代碼,我們點擊 test
按鈕,發現頁面會觸發更新,同時渲染出對應的的內容。表明我們設置成功了,如果還有在深層次的嵌套,同樣需要依次遍歷對應的值,然後使用$set
設置即可。
表單驗證
對於 element-ui 的表單驗證,我們需要在 html 上添加一些屬性
<div class="test">
<el-form ref="form" :model="form" label-width="80px" :rules="rules">
<div class="parent" v-for="(parent, index) in form.list" :key="`parent-${index}`">
<div class="child" v-for="(child, idx) in parent" :key="`child-${idx}`">
<el-form-item label="name" prop="name">
<el-input v-model="child.name"></el-input>
</el-form-item>
<el-form-item label="age">
<el-input v-model="child.age"></el-input>
</el-form-item>
</div>
</div>
</el-form>
<el-button type="primary" @click="testFun">test</el-button>
</div>
// ...script
// data
rules: {
name: {
required: true,
message: "請輸入值",
trigger: "blur"
}
}
- 第一步,首先在
el-form
上,添加:rules="xxx"
,xxx 可以隨便設置,這裏我設置爲rules
。 - 第二部,在要驗證的表單
item
上,添加prop
,這個prop
對應rules
裏面的值,這裏設置爲name
。
其實到這裏已經設置完了,我們發現,在只有一個 name
表單的情況下,能夠正常驗證(沒有通過 v-for 遍歷),但是如果是多個表單同時都指向 name
呢 ?
所以我們可以在單個的表單域上傳遞屬性的驗證規則:
<el-form-item
label="name"
:prop="`list.${index}.${idx}.name`"
:rules="{ required: true, message: '請輸入郵箱地址', trigger: 'blur' }">
<el-input v-model="child.name"></el-input>
</el-form-item>
在 rules
上,我們設置爲單獨的 rules
,而 prop
比較麻煩,需要以 index
和 .
的方式來取對應的字段,這裏需要注意。
那麼通過以上設置後,我們就可以對每個 name
字段分別進行驗證了。