目录
- 1. 需求分析
- 2. 表单功能的简单实现
- 3. 抽象验证规则
- 4. 支持 v-model 双向绑定
- 5. 使用 $attrs 支持默认属性
- 6. 父组件调用子组件中的方法
1. 需求分析
我们要把我们的表单组件分成两个部分,一个是item部分,一个是整体的 form 部分,form部分由item和button提交按钮共同组成。
在我们单击每个输入框时会触发每一个item的验证规则,然后点击登录按钮会验证整个 form 。
2. 表单功能的简单实现
我们先去 bootstrap 文档里找到 form 表单然后把它的模板代码 copy 过来,当然前提是我们首先要在项目中安装 BootStrap。
现在运行我们的项目,就能看到 form 表单的样式了:
首先我们通过 reactive 来绑定每个输入框需要绑定的数据:
const emailRef = reactive({ val: '', error: false, message: '' })
然后通过 v-model 和我们刚刚定义的数据进行双向绑定:
我们又定义了一个 validateEmail 函数当鼠标失去焦点时触发,我们在这个方法中定义输入框的验证标准:
const emailReg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/ const validateEmail = () => { if (emailRef.val.trim() === '') { emailRef.error = true emailRef.message = '输入内容不能为空' } else if (!emailReg.test(emailRef.val)) { emailRef.error = true emailRef.message = '输入邮箱格式不正确' } else { emailRef.error = false } }
现在当我们什么也不输入时,输入框效果:
输入邮箱格式不正确时,输入框效果:
3. 抽象验证规则
刚刚我们完成了邮箱的验证逻辑,我们还得做密码框的验证逻辑,如果我们的表单还有很多功能不一样的输入框,那我们得挨个给他们添加验证功能,这样就有大量重复代码,要写非常多冗余的变量和函数,我们作为开发者最忌讳的就是复制粘贴做搬运工,所以我们就想把这部分的逻辑抽离出去作为一个可复用的组件
我们输入框的组件就是图片中的 validate-input,如果我们只需要在父组件中输入要验证的规则和验证失败的信息,把逻辑交给 validate-input 来判断,那整体代码就非常清晰了。
我们通过 rules 属性来传给组件指定验证类型。message字段是出现问题时提示的内容,因为我们的输入框组件可以使用不止一种规则,所以 RulesProp 应该是 RuleProp 的数组。如果以后要添加其他的规则,就可以直接在下面的 type 中添加,这样可扩展性非常高。
interface RuleProp { type: 'required' | 'email'; message: string; } export type RulesProp = RuleProp[]
我们在子组件中定义规则的接口,然后定义都是这种类型的数组结构并把它导出出去方便父组件使用。如果不熟习 typescript 的朋友,就可以把它当作定义一个RuleProp对象,里面有两个属性,一个是规则类型,一个是出现问题时提示的内容。然后再定义一个对象数组,把它导出出去这样父组件向子组件传递的都是这种规定的对象数组。
子组件接受的 props把它断言成 RulesProp 类型的数组:
我们再看一下 validate-input 的逻辑部分:
setup (props) { const inputRef = reactive({ val: '', error: false, message: '' }) const validateInput = () => { if (props.rules) { const allPassed = props.rules.every(rule => { let passed = true inputRef.message = rule.message switch (rule.type) { case 'required': passed = (inputRef.val.trim() !== '') break case 'email': passed = emailReg.test(inputRef.val) break default: break } return passed }) inputRef.error = !allPassed } } return { inputRef, validateInput } }
我们先定义一个 inputRef 对象来绑定输入信息和状态。validateInput 当输入框失去焦点的时候触发这个验证函数。下面我们来看一下这个函数的实现逻辑:
首先通过一个 if 实现当有 props 的时候才做验证。然后通过数组的 every 方法来给每一个数组中的每一个验证对象做判定,every 方法如果全部为真时才为真,有一个为假就是假。他很符合表单验证的逻辑,所以最后 every 方法一定会返回 true 或者 false,我们让一个变量接受它,如果这个变量是真就代表输入框全部验证规则都通过,那么 inputRef 的 error 属性就是 false,这样就不会如下的错误提示:
<span v-if="inputRef.error" class="invalid-feedback">{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->{inputRef.message}}</span>
在 every 方法中,我们一项一项判断,先设置当前项返回 true ,然后把当前项的 message ,也就是错误提示内容赋值给 inputRef 的 message,然后通过 switch 来判定当前项的状态,这样当当前项不满足规则时,返回的 message 就是当前项的 message
如果最后 inputRef.error 是 false,那么就添加 bootstrap 中表单错误类,来实现动态绑定:
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" v-model="inputRef.val" :class="{'is-invalid':inputRef.error}" @blur = "validateInput" >
到这里我们就实现了子组件的处理,下面只需要在父组件中把参数传过去就行了:
const emailRules: RulesProp = [ { type: 'required', message: '电子邮箱地址不能为空' }, { type: 'email', message: '请输入正确的电子邮箱格式' } ] const passwordRules: RulesProp = [ { type: 'required', message: '输入密码不能为空' } ]
我们定义两个 RulesProp 类型的数组作为参数,传递给子组件:
现在启动项目,看一下效果:
这样我们这一节抽离验证规则的目的就达到了。
4. 支持 v-model 双向绑定
现在我们已经把验证规则抽离出来,实现了表单的基本验证,但是有一个痛点需要我们解决,我们在父组件中现在拿不到用户在输入框中输入的值,这样就实现不了下一步的其他需求,在 input 中我们通过 v-model 指令来进行双向绑定可以很轻松地获得用户输入的值,那么在 validate-input 组件中我们如何来实现 v-model 呢?
我们先看一下 vue3 中 v-model 的实现原理:
vue3 中摒弃了 vue2 里通过动态绑定 input 的 value 属性和 input 事件实现的双向绑定,通过 modelValue 这么个属性和 onUpdate:modelValue 这个事件来实现双向绑定。所以我们要实现 v-model 就要有上面的属性和更新输入框的时候出发的事件。
首先我们来写 validate-input 子组件中的内容,在 props 参数中加入 modelValue:
然后用 :value 和 input 事件把原来 input 中的 v-model 替换一下:
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" :value="inputRef.val" :class="{'is-invalid':inputRef.error}" @blur = "validateInput" @input="updateValue" >
定义 input 事件,注意提交的事件名为 update:modelValue:
const updateValue = (e: KeyboardEvent) => { const targetValue = (e.target as HTMLInputElement).value inputRef.val = targetValue context.emit('update:modelValue', targetValue) }
我们在父组件中使用一下 v-model ,看看效果:
可以看到我们成功实现了 v-model 的双向绑定:
5. 使用 $attrs 支持默认属性
在原生 input 中有很多属性,比如 placeholder ,如果我们在我们的输入框组件中添加这个属性,会正常显示在页面上吗?我们试一下:
启动项目,查看效果:
placeholder没有正常显示出来,我们查看控制台,看看哪里有问题:
可以看到我们的placeholder被直接添加到 input 的父级上了,那如何把属性正确添加在 input 上呢?
1. 首先我们只需要在组件的选项中设置 inheriAttrs: false:
2. 通过 $attrs 把属性添加到元素上:
我们先输出一下 $attrs 看看里面有什么:
这是一个响应式对象,里面包括了我们传递给子组件的属性
下面我们先通过 v-bind 绑定 $attrs :
给我们的 input 组件添加属性:
启动项目,查看输出:
现在成功给组件添加了 placeholder 属性,也成功添加了 type ,密码框也变成了小圆点。
6. 父组件调用子组件中的方法
现在我们要实现的就是点击提交按钮,然后分别进行两个输入框的验证。可是我们的验证方法在 validate-input 这个子组件中,所以我们就得在父组件中调用子组件里的方法来实现表单验证。
1. 给子组件添加 ref 属性:
2. 给提交按钮添加点击事件:
3. 在 setup 中定义响应式对象及点击事件:
const emailChild = ref<InstanceType<typeof ValidateInput>>() const passwordChild = ref<InstanceType<typeof ValidateInput>>() const ensureForm = () => { emailChild.value?.validateInput() passwordChild.value?.validateInput() }
这样我们就成功调用了子组件中的 validateInput 方法,实现了我们想要的效果。
4. 启动项目,查看效果:
直接点击提交按钮:
输入错误邮箱格式,点击提交:
到这里我们的表单组件就开发完成了。
到此这篇关于Vue自定义Form组件实现方法介绍的文章就介绍到这了,更多相关Vue自定义Form组件内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!