# [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)