Vue进阶(二):仿写ElementUI表单-实现表单验证组件

前言:

form表单验证是几乎所有web项目或者APP都会遇到的,这是非常常用也是非常重要的一项知识,这里我们简单学习一下表单验证的方法。

async-validator是一个表单的异步验证的第三方库,它是 https://github.com/tmpfs/async-validate 的演变。也是 element-ui 中的form组件所使用的验证方式。

我们就仿写一个element-UI的表单验证功能。

  • 当没有清空输入的时候提示 请输入XXX

image-20200326231658419

  • 点击提交的时候,进行一个验证。

安装插件

我首先还是使用了vue脚手架,因为表单验证,我们使用了 async-validator 所以这里我们要先安装一下这个第三方库

1
npm install --save async-validator

并且建立好我们的文件目录结构:

image-20200326232018275

实现方法

搭建基本框架

  • 首先是我们的 App 全局组件:
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
<template>
<div id="app">
<h3>Element表单</h3>
<hr>
<!-- 在最外层对model和rules进行数据绑定 -->
<el-form :model="model" :rules="rules" ref="loginForm">
<el-form-item label="用户名" prop="username">
<!-- v-model把可以看作:value 和 @input的语法糖 把model.username作为value传入 -->
<el-input v-model="model.username" ></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="password">
<el-input type="password" v-model="model.password"></el-input>
</el-form-item>
<el-form-item>
<button type="primary" @click="submitForm('loginForm')">提交</button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import ElInput from './components/ElInput.vue'
import ElForm from './components/ElForm'
import ElFormItem from './components/ElFormItem'
export default {
name: 'App',
components: {
ElInput,
ElForm,
ElFormItem
},
data() {
return {
model: {username: '',password: ''},
// 校验规则
rules: {
username:[
{required: true, message: '请输入用户名', trigger: 'blur'}
],
password:[
{required: true, message: '请输入密码', trigger: 'blur'}
]
}
}
}
}
</script>
<style>
</style>
  • 然后 ElForm 组件 ,我们接收 App 组件传来的值,并进行provide。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<form>
<slot></slot>
</form>
</template>
<script>
export default {
name: 'ElForm',
provide() {
return {
form: this
}
},
// 接收App传来的数据
props:['model', 'rules']
}
</script>
<style scoped>
</style>
  • 然后是 ElInput 组件,非常简单的的一个功能,实现双向绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 绑定value属性 使用的是父组件传入的参数 实现input事件 派发事件 固定写法 -->
<template>
<input :type="type" :value="value" @input="onInput" @blur="onBlur">
</template>
<script>
export default {
name: 'ElInput',
// 接受父组件传来的值
props: {
value: {
type: String,
default: ''
},
type: {
type: String,
default: 'text'
}
}
}
</script>
<style scoped>
</style>
  • 然后在 ELFormItem 组件中设置插槽,插入我们的 ElInput 组件,展示我们的错误信息:
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
<template>
<div>
<!-- 父组件传值 -->
<label v-if="label">{{label}}</label>
<slot></slot>
<!-- 错误信息 是自身的属性 -->
<p v-if="error" style="color:red">{{errortext}}</p>
</div>
</template>
<script>
import Schema from 'async-validator'
export default {
name: 'ElFormItem',
// 注入数据
inject: ['form'],
// 接收参数
props:['label', 'prop'],
data() {
return {
errortext: '',
error: false
}
}
</script>
<style scoped>
</style>

这样我们的基本结构就搭建好了。

实现逻辑

  • 首先我们在 ElInput 组件中派发事件,这里我们使用 $dispatch ,所以要先在 main.js 中添加该方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
Vue.prototype.$dispatch = function(eventName, data) {
let parent = this.$parent
while(parent) {
parent.$emit(eventName, data)
parent = parent.$parent
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
  • 然后我们就可以在 ElInput 组件中派发事件:
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
<template>
<input :type="type" :value="value" @input="onInput" @blur="onBlur">
</template>
<script>
export default {
name: 'ElInput',
props: {
value: {
type: String,
default: ''
},
type: {
type: String,
default: 'text'
}
},
methods: {
onInput(e) {
// 派发消息input 和 输入框的值作为参数
this.$emit('input', e.target.value)
// 通知校验
this.$dispatch('validate')
}
}
}
</script>
<style scoped>
</style>
  • 首先我们在 ElFormItem 组件中进行校验。
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
<template>
<div>
<label v-if="label">
{{label}}
</label>
<slot></slot>
<p v-if="error" style="color:red">{{errortext}}</p>
</div>
</template>
<script>
// 引入第三方库
import Schema from 'async-validator'
export default {
name: 'ElFormItem',
inject: ['form'],
props:['label', 'prop'],
data() {
return {
errortext: '',
error: false
}
},
methods: {
//当输入时 子类通过$parent派发的 本身负责接收然后 校验
validate() {
// 获取rules,校验规则
const rules = this.form.rules[this.prop]
// 获取数据模型
const value = this.form.model[this.prop]
// 定义一个descriptor
let desciptor = { [this.prop]:rules }
const schema = new Schema(desciptor)
//返回的是promise
return schema.validate({[this.prop]:value}, errors => {
if(errors) {
this.errortext = errors[0].message
this.error = true
} else {
this.errortext = ''
this.error = false
}
})
},
},
mounted() {
this.$on('validate', this.validate)
}
}
</script>
<style scoped>

</style>
  • 然后在 ElForm 组件中做全局校验:
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
<template>
<form>
<slot></slot>
</form>
</template>
<script>
export default {
name: 'ElForm',
provide() {
return {
form: this
}
},
props:['model', 'rules'],
methods: {
//做全局校验 cb是传进来的函数
validate(cb){
//获取校验项
//首先拿到KForm的子元素 拿去遍历 得到的是每一个FormItem 让它执行validate方法
//返回的是一个promise数组
const tasks = this.$children
.filter(item=>item.prop)
.map(item=>item.validate())
//Promise.all所有promise成功才算成功
Promise.all(tasks)
.then(()=>cb(true))
.catch(()=>cb(false))
}
}
}
</script>
<style scoped>
</style>
  • 最后在 App 组件中进行提交:
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
<template>
<div id="app">
<h3>Element表单</h3>
<hr>
<el-form :model="model" :rules="rules" ref="loginForm">
<el-form-item label="用户名" prop="username">
<el-input v-model="model.username" ></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="password">
<el-input type="password" v-model="model.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<button type="primary" @click="submitForm('loginForm')">提交</button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import ElInput from './components/ElInput.vue'
import ElForm from './components/ElForm'
import ElFormItem from './components/ElFormItem'
export default {
name: 'App',
components: {
ElInput,
ElForm,
ElFormItem
},
data() {
return {
model: {username: '',password: ''},
rules: {
username:[
{required: true, message: '请输入用户名', trigger: 'blur'}
],
password:[
{required: true, message: '请输入密码', trigger: 'blur'}
]
}
}
},
methods: {
submitForm(formName) {
this.$refs[formName].validate(valid=>{
if(valid) {
alert('校验成功')
} else {
alert('校验失败')
}
})
}
}
}
</script>
<style>
</style>

结语:

一个简单的表单验证功能就完成了,这里面有很多都是第三方库的知识,很多都是一个固定的写法,你可以查看一下参考资料,进行一个详细的学习。

参考资料:

Element-UI表单:https://element.eleme.cn/#/zh-CN/component/form

校验规则:https://github.com/yiminghe/async-validator

------ 本文结束  感谢阅读 ------