# [Vue 2] Element UI 巢狀表單驗證筆記
###### tags: `Vue` `前端筆記` `Element UI`
==目前接手專案很舊,所以用的是 Vue 2 的寫法。==
## 巢狀表單驗證
### 1. 一般的情況(單層)
如果只是單層的話就是直接 `prop="proertyName"` 即可。
```javascript=
<el-form :model="form" ref="inoEditForm" :rules="inoEditRules">
<el-form-item prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
</el-form>
form: {
name: '',
sex: '',
age: ''
}
```
填寫驗證的手段:
```javascript=
// ...
data() {
return {
// ...
inoEditRules: {
// 每個欄位都是一個 keyName: [{ required?: boolean, validation?: Function, tiggier: string | string[], message?: string}]
whiteListName: [
{
required: true,
validator: validateWhiteListName, // 這邊是放客製化驗證的函式
trigger: ['blur', 'change']
},
],
}
}
// ...
```
### 2. 巢狀的欄位(不只單層)
如果巢狀的話就要給 `prop="第二層 propertyName.指定 propertyName"`,這樣子 Element UI 的 `<el-form>` 就會自己去抓對應的 propety 驗證。
```javascript=
<el-form :model="form" ref="inoEditForm" :rules="inoEditRules">
<el-form-item prop="user_info.name">
<el-input v-model="form.user_info.name"></el-input>
</el-form-item>
</el-form>
form: {
user_info: {
name: '',
sex: '',
age: ''
}
}
```
填寫驗證的手段:
```javascript=
// ...
data() {
return {
// ...
inoEditRules: {
// 每個欄位都是一個 keyName: [{ required?: boolean, validation?: Function, tiggier: string | string[], message?: string}]
// 有「,」因此需要手轉字串
'user_info.name': [
{
required: true,
validator: validateWhiteListName, // 這邊是放客製化驗證的函式
trigger: ['blur', 'change']
},
],
}
}
// ...
```
### 3. 陣列產生的欄位
這個時候就必須塞 `expression` 組對應的欄位名稱 `:prop="'user_info.' + index + '.name'"` 假設 `index` 為 0 的話就會得到 `'user_info.0.name'`,這樣子 Element UI 才會找到對應的 `form` 欄位驗證。
```javascript=
<el-form :model="form" ref="inoEditForm" :rules="inoEditRules">
<el-form-item
v-for="(info, index) in form.user_info"
:prop="'user_info.' + index + '.name'"
>
<el-input v-model="info.name"></el-input>
</el-form-item>
</el-form>
form: {
user_info: [
{
name: '',
sex: '',
age: ''
},
{
name: '',
sex: '',
age: ''
}
]
}
```
填寫驗證的手段:
```javascript=
// ...
data() {
return {
// ...
inoEditRules: {
// 每個欄位都是一個 keyName: [{ required?: boolean, validation?: Function, tiggier: string | string[], message?: string}]
// 有「,」因此需要手轉字串
'user_info.0.name'': [
{
required: true,
validator: validateWhiteListName, // 這邊是放客製化驗證的函式
trigger: ['blur', 'change']
},
],
// 最多有多少欄位就要先寫好有多少驗證
'user_info.1.name'': [
{
required: true,
validator: validateWhiteListName, // 這邊是放客製化驗證的函式
trigger: ['blur', 'change']
},
],
}
}
// ...
```
### 4. 開大決
如果動態的表單是無限多,或者超級多的話,總不可能先在檔案中手寫表(因為不可能會寫完...),這個時候就可以用藉由 template 的協助,直接把 `props` 寫在 template 內,讓動態生成的欄位也可以套用表單驗證的格式:
```javascript=
// template
// ... 可以無限點擊按鈕新增 item
<div v-for="(_, index) in list" :key="key + objectIndex">
<p>{{ key.toUpperCase() }}</p>
// STEP 2: template prop 及 rules 綁定
<el-form-item :prop="`tasks.${index}.name`" :label="`${index}_name`" :rules="rules.taskNameRules">
<el-input v-model="form.tasks[index].name" style="width: 100%"></el-input>
</el-form-item>
</div>
// script
data() {
return {
...
form: {
...,
// STEP 1: 定義好 template 綁定的欄位,切記,在 template 綁定時對應的 :prop="keyName.index.targetFieldName"
tasks: [{ name: '' }],
...
}
rules: {
// STEP 3: 之後動態新增的欄位就會套用這個驗證
taskNameRules: [
{ required: true, trigger: ['blur', 'change'], validator: customValidationFunc }
]
}
}
}
```
這個時候厲害的 Element UI 就會讓動態新增的表單也吃到 `rules.taskNameRules` 中陣列的驗證了,不過必須要注意 template 綁定的 `:prop` 必須要對好才可以。
## 額外補充
### 動態生成表單 + 驗證需要依賴其他欄位
如果當前表單是動態產生,且驗證時不僅需要驗證自身欄位,還需要驗證其他欄位時,可以藉由 `<template>` 渲染時會叫用 `<template>` 上的函式 + `validator` 會依照滿足 `trigger` 事件後才叫用的原理,直接先建立一個立即函式,透過函式作用域(functional scope)的特性,日後 `validator` 因滿足 `trigger` 事件被呼叫時就可以讀取到被依賴的欄位:
```javascript=
<el-col v-for="(_, index) in list">
<el-form-item label="發放總額" :prop="`list.${index}.sendTotal`" :rules="rules.sendTotal"
:rules="{ required: true, trigger: ['blur', 'change'], validator: (rule, value, callback) => { return ((index) => {
if (Number(value) < Number(form.tasks[index].sendLimit)) return callback('單筆獎勵發送數量不可大於發放總額')
return callback()
})(index)}}">
<el-input v-model="form.tasks[index].sendTotal"></el-input>
</el-form-item>
<el-form-item label="單筆獎勵發送數量" :prop="`tasks.${index}.sendLimit`" :rules="rules.sendLimit">
<el-input v-model="form.tasks[index].sendLimit"></el-input>
</el-form-item>
</el-col>
```
```javascript=
{
required: true,
trigger: ['blur', 'change'],
// validator 叫用後,就會叫用裡面的立即函式,其 parameter 是 index,以便從 index 撈依賴的欄位是否符合驗證條件
validator: (rule, value, callback) => { return ((index) => {
if (Number(value) < Number(form.tasks[index].sendLimit)) return callback('單筆獎勵發送數量不可大於發放總額')
return callback()
})(index)
}
```
#### 會有抓不到最新值的問題嗎?
不會,因為 `validator` 呼叫時都會重現呼叫立即函式,所以每次都會從 `index` 拿到新的值。
## 參考資料:
1. [ElementUI表单校验prop的嵌套写法](https://segmentfault.com/a/1190000023851366)
2. [el-form表单对象内还嵌套对象,绑定的prop规则校验会失效](https://blog.csdn.net/weixin_42190844/article/details/121532087)
3. [element ui动态表单 + 自定义校验规则](https://blog.csdn.net/qiankui/article/details/90737748)