上一节我们为组件开发做了技术铺垫,这一节开始我们来进入实战环节
首先我们来实现一套Form组件,如果你使用过市面上比较流行的组件库,你应该知道一套Form组件应该包含这样的内容
那么我们首先下一个用例,从用例出发,倒推组件
<f-form :model="userForm" :rules="rules">
<f-form-item label="用户名" prop="username">
<f-input v-model="userForm.username" placeholder="请输入用户名" />
</f-form-item>
<f-form-item label="密码" prop="password">
<f-input type="password" v-model="userForm.password" placeholder="请输入密码" />
</f-form-item>
</f-form>
<script>
export default {
data() {
return {
userForm: {
username: '',
password: ''
},
rules: {
username: [
{required: true, message: '请输入用户名'}
],
password: [
{required: true, message: '请输入密码'}
]
}
}
}
}
</script>
从内而外实现,从input开始,然后form-item,最后form,话不多说开搞
f-input
首先来输入框,你可能会觉着这东西很简单,但是不要忘了我们之前说的,子组件不能直接修改prop接收的值,所以赶紧抛弃你直接v-model绑定prop的想法。
如果你不关闭警告你会看到这样的警告
这里你需要知道,其实v-model是一个语法糖,下面这两行其实作用是一样的
1<input v-model="username" />
<br/>
2<input :value="username" @input="username = $event.target.value" />
<br/>
{{username}}
运行一下看下结果
所以在f-input中你可以这样来实现绑定的传递
<template>
<div>
<input :value="value" @input="inputHandler" />
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
methods: {
inputHandler(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
这时候再去试试,发现已经可以了,控制台也没有报警告
然后你又发现了一个问题,设置的placeholder没有生效,因为他被添加到f-input上,而不是f-input里面的input上
官网API文档有这么一段
它的作用总结下来就是,如果开启此选项,父组件绑定的属性没有被props接收的,将会存在attrs绑定在任意非根组件上,然后把我们的f-input稍加改造
<template>
<div>
<input :value="value" @input="inputHandler" v-bind="$attrs" />
</div>
</template>
<script>
export default {
inheritAttrs: true,
// 省略部分代码
</script>
完美,可以看到设置的密码型输入框也已经生效了
f-form-item
到这里就比上面的输入框稍微有点难度了,但也很简单,这里需要实现的点是
- label文本
- 给表单组件预留插槽
- 按规则校验并提示错误
前两点都很简单,你应该比较畏惧第三点,其实这个也很简单,有现成的“轮子”——async-validator,作为程序员要杜绝重复造轮子😂
想一下我们之前的用例中form-item的属性:label和prop,然后配置要接收的参数
<template>
<div>
<label v-if="label">{{label}}</label>
<slot></slot>
<p v-if="errorMessage">{{errorMessage}}</p>
</div>
</template>
<script>
export default {
props: {
label: {
type: String,
default: ''
},
prop: {
type: String,
}
},
data() {
return {
errorMessage: ''
}
},
}
</script>
这样我们第一步和第二步都完成了,接下来就是validate了,接受的prop的作用就是为了从form组件中绑定的rules中获取需要使用的验证规则,同时也使用prop从form绑定的表单内容中获取当前表单项的值。这里可以使用provide/inject来传递数据
<script>
import Schema from 'async-validator'
export default {
inject: ['form'],
props: {
label: {
type: String,
default: ''
},
prop: {
type: String,
}
},
data() {
return {
errorMessage: ''
}
},
methods: {
validate() {
const rule = this.form.rules[this.prop]
const value = this.form.model[this.prop]
// 获取验证规则实例
const description = {[this.prop]: rule}
const schema = new Schema(description)
// 使用验证规则实例的验证方法
return schema.validate({[this.prop]: value}, (error, field) => {
if(error) {
this.errorMessage = error[0].message
console.log(`${field}验证未通过`);
} else {
this.errorMessage = ''
}
})
}
},
}
</script>
然后添加验证事件的监听器,在内部表单项值修改时动态验证
mounted() {
this.$on('validate', this.validate)
}
再顺便修改一下f-input的内容
methods: {
inputHandler(e) {
this.$emit('input', e.target.value)
// 通知验证
this.$parent.$emit('validate')
}
}
OK~~form-item齐活
f-form
在完成了form-item和input组件之后,form组件就简简单单了,再来看一下form要做的事
- 接收验证规则和表单数据
- 给form-item预留插槽
- 全局校验方法
首先我们来实现前两步,很简单,不要忘了之前form-item使用inject接收的数据,这里需要绑定上
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
provide() {
return {
form: this
}
},
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
}
}
</script>
接下来全局的校验,接受一个回调函数,回调函数的参数是一个布尔值,代表校验是否通过,使用Promise.all来执行多个验证器,只要有失败的就返回false
methods: {
validate(cb) {
const validators = this.$children
.filter(item => item.prop)
.map(item => item.validate())
Promise.all(validators)
.then(() => cb(true))
.catch(() => cb(false))
}
},
然后在修改一下用例,添加一个提交按钮,绑定全局校验
<template>
<div>
<f-form :model="userForm" :rules="rules" ref="formRef">
<f-form-item label="用户名" prop="username">
<f-input v-model="userForm.username" placeholder="请输入用户名" />
</f-form-item>
<f-form-item label="密码" prop="password">
<f-input type="password" v-model="userForm.password" placeholder="请输入密码" />
</f-form-item>
<button @click="submit">提交</button>
</f-form>
</div>
</template>
<script>
// 省略部分代码
methods: {
submit() {
this.$refs.formRef.validate(valid => {
if(valid) {
console.log('通过验证');
} else {
console.log('验证失败');
}
})
}
},
}
</script>
到现在我们就已经粗略的完成了一套form组件,当然他的功能并不止于此,这只是起了个头具体功能完善也就不在这里细说了,效果展示如下,样式细节也不在这里扣了
本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情