我是靠谱客的博主 开心皮带,这篇文章主要介绍遍历的子表单,对应的校验规则不同,父表单提交时的校验写法,现在分享给大家,希望可以做个参考。

场景介绍

某些情况下我们会遇到一些特殊的需求场景。
外面是个大表单:包含邮箱和对应的答案
答案这里是遍历出来的子表单,点击新增就新增一个分类答案
但是通用答案里 只有答案是必填的,选择区域是非必填
其他的分类答案里答案和选择区域都是必填的。
这里遍历的子表单的规则就有差异了,而且还是子表单,点击保存的时候我们是在父表单那里判断是否通过校验。

(tips: 对于大佬来说可能很简单,但是我第一次遇到这种需求,我原本的思路错了,导致我卡了三个小时,第二天早上来的时候才看着elementUI官网的form想出来怎么实现的)
在这里插入图片描述
在这里插入图片描述

rules.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const rules = { email: [ { required: true, message: '请输入邮箱地址', trigger: 'blur' }, { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur'] } ], answer: [ {required: true, message: '请输入答案', trigger: 'blur'}, {min: 1, max: 1000, message: '请输入 1 到 1000 个字', trigger: 'blur'}, ], region: [ {required: true, message: '请绑定客户标签', trigger: 'blur'}, ] };

Form1.vue // 父级表单

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<template> <el-form ref="dynamicValidateForm" :model="dynamicValidateForm" class="demo-dynamic" label-width="80px" :rules="rules" > <el-form-item prop="email" label="邮箱" > <el-input v-model="dynamicValidateForm.email"></el-input> </el-form-item> <div class="answer-tab-container"> <span v-for="(_item, index) in dynamicValidateForm.list" :key="`list-${index}`" :class="['list', index === activeAnswerIndex ? 'is-active' : '']" @click="changeActiveAnswer(index)" > {{ index ? '分类答案' + index : '通用答案' }} </span> <el-button type="text" @click="addItem" > <span class="btn-text"> + 新增 </span> </el-button> </div> <answer-item v-for="(item, index) in dynamicValidateForm.list" v-show="index === activeAnswerIndex" ref="answerRef" :key="(item.id || item.key) + 'answer'" :data="dynamicValidateForm" :answer-item="item" :current-index="index" :is-last-answer="index === dynamicValidateForm.list.length - 1" class="answer-editor" @remove="removeItem" /> <div class="footer"> <el-button type="primary" @click="submitForm" > 保存 </el-button> <el-button> 取消 </el-button> </div> </el-form> </template> <script> import AnswerItem from './components/AnswerItem'; import {rules} from './rules'; export default { components: { AnswerItem }, data() { return { rules, activeAnswerIndex: 0, dynamicValidateForm: { email: '', list: [ { answer: '', key: Math.random(), region: [] } ] } }; }, mounted() { if (!this.dynamicValidateForm.list?.length) { this.dynamicValidateForm.list = []; this.addItem(); } }, methods: { /** * 表单校验 * @param [forms='form'] 要校验的表单,可以是字符串或者ElForm实例或者带有validate校验方法的组件 * @param [callback] 校验表单后的回调函数,当传入该值时执行结果Promise一直为resolved * @return Promise */ $validate(this, forms = 'forms', callback = null) { if (typeof forms === 'function') { callback = forms; forms = ''; } if (!(forms instanceof Array)) { forms = [forms]; } let newForms = forms.map(form => { if (typeof form === 'string') { return this.$refs[form || 'form']; } return form; }).filter(v => v && v.validate); let need_valid = newForms.length; let in_valid = false; return new Promise((resolve, reject) => { function cb(valid, detail = null) { need_valid -= 1; if (in_valid) { return; } if (typeof valid === 'undefined') { valid = true; } if (valid && typeof valid !== 'boolean' && !detail) { detail = valid; valid = false; } if (!valid) { console.warn('$validate', detail); in_valid = true; let error = new Error('数据校验失败,请检查填写内容'); if (typeof callback === 'function') { callback(false, detail); resolve(error); } else { reject(error); } return; } if (need_valid === 0) { if (typeof callback === 'function') { callback(true); } resolve(true); return; } } // 放入下一帧执行,避免立即校验出错时浏览器 paused on promise rejection this.$nextTick(() => { newForms.forEach(form => form.validate(cb)); if (newForms.length === 0) { need_valid = 1; cb(true); } }); }); }, changeActiveAnswer(index) { this.activeAnswerIndex = index; }, addItem() { const newItem = { answer: '', key: Math.random(), tags: [] }; this.dynamicValidateForm.list.push(newItem); this.changeActiveAnswer(this.dynamicValidateForm.list.length - 1); }, removeItem(index) { if (index === 0) { return; } this.dynamicValidateForm.list.splice(index, 1); this.changeActiveAnswer(index - 1); }, async submitForm() { try { await this.$validate(this.$refs.dynamicValidateForm); await Promise.all( this.$refs.answerRef.map( form => this.$validate(form.$refs.answerForm) ) ); } catch (e) { console.log('e', e) this.$message.warning('请正确填写后才能保存'); return; } this.$message.success('成功'); }, } }; </script> <style lang="less" scoped> .demo-dynamic { margin: 20px auto 0; width: 800px; .list { border: 1px solid black; &.is-active { background-color: aqua; } } } </style>

AnswerItem.vue // 子表单

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template> <el-form v-if="data" ref="answerForm" :model="data" label-width="90px" > <el-form-item :label="'答案' + (currentIndex || '')" :prop="'list.' + currentIndex + '.answer'" :rules="rules.answer" > <el-input v-model="answerItem.answer"></el-input> <el-button v-if="currentIndex > 0" type="text" class="ml10" @click="onRemove(currentIndex)" > 删除 </el-button> </el-form-item> <el-form-item :key="(answerItem.id || answerItem.key) + 'region'" label="选择区域" :prop="'list.' + currentIndex + '.region'" :rules="currentIndex ? rules.region : {}" > <el-select v-model="answerItem.region" placeholder="请选择区域"> <el-option label="区域一" value="shanghai"></el-option> <el-option label="区域二" value="beijing"></el-option> </el-select> </el-form-item> </el-form> </template> <script> import {rules} from '../rules'; export default { props: { data: { type: Object, default: null }, answerItem: { type: Object, default: null }, currentIndex: { type: Number, default: -1 } }, data() { return { rules }; }, mounted() { }, methods: { onRemove(index) { this.$emit('remove', index); } } }; </script> <style lang="less" scoped> </style>

因为答案这块我们是v-for 遍历出来的,所以我们的答案和选择区域对应的是 dynamicValidateForm.list 数组。因此我们可以给动态的给prop,

复制代码
1
2
:prop="'list.' + currentIndex + '.answer'"

这样我们通用答案和分类答案都会对应数组中自己的位置。不过他们在哪都是必填的,所以rules就可以写死: rules=“rules.answer”
然后就是我们的选择区域,他这里通用答案和分类答案对应的规则就不一样了,所以这里的rules要给不同的

复制代码
1
2
3
:rules="currentIndex ? rules.region : {}" // 最简单的方法就是根据下标判断,currentIndex 为0 就表示是第一项 也就是通用答案 菲比条 所以给{}。 其余的都要求必填 就走 rules.region的规则就行

回到父组件这里,我们想要保存肯定要看里面的校验都通过没有

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async submitForm() { try { await this.$validate(this.$refs.dynamicValidateForm); await Promise.all( this.$refs.dynamicValidateForm.map( form => this.$validate(form.$refs.dynamicValidateForm) ) ); } catch (e) { this.$message.warning('请正确填写后才能保存'); return; } // 校验都通过了 这里可以写想要执行的方法 this.$message.success('成功'); },

示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个问题记录一下,毕竟卡了我四个多小时加班到九点多也没整出来,第二天早上九点到了之后干了半小时干出来的。
这是我第一次见到 props也能动态赋值的,
还是根据数组的下标: 数组.下标.键名 这样动态赋值的

最后

以上就是开心皮带最近收集整理的关于遍历的子表单,对应的校验规则不同,父表单提交时的校验写法的全部内容,更多相关遍历内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部