Vue自定义Form组件实现方法介绍

作者:YinJie… 时间:2024-04-28 09:21:14 

1. 需求分析

我们要把我们的表单组件分成两个部分,一个是item部分,一个是整体的 form 部分,form部分由item和button提交按钮共同组成。

Vue自定义Form组件实现方法介绍

在我们单击每个输入框时会触发每一个item的验证规则,然后点击登录按钮会验证整个 form 。

2. 表单功能的简单实现

我们先去 bootstrap 文档里找到 form 表单然后把它的模板代码 copy 过来,当然前提是我们首先要在项目中安装 BootStrap。

Vue自定义Form组件实现方法介绍

现在运行我们的项目,就能看到 form 表单的样式了:

Vue自定义Form组件实现方法介绍

首先我们通过 reactive 来绑定每个输入框需要绑定的数据:

const emailRef = reactive({
     val: '',
     error: false,
     message: ''
   })

然后通过 v-model 和我们刚刚定义的数据进行双向绑定:

Vue自定义Form组件实现方法介绍

我们又定义了一个 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
     }
}

现在当我们什么也不输入时,输入框效果:

Vue自定义Form组件实现方法介绍

输入邮箱格式不正确时,输入框效果:

Vue自定义Form组件实现方法介绍

3. 抽象验证规则

刚刚我们完成了邮箱的验证逻辑,我们还得做密码框的验证逻辑,如果我们的表单还有很多功能不一样的输入框,那我们得挨个给他们添加验证功能,这样就有大量重复代码,要写非常多冗余的变量和函数,我们作为开发者最忌讳的就是复制粘贴做搬运工,所以我们就想把这部分的逻辑抽离出去作为一个可复用的组件

Vue自定义Form组件实现方法介绍

我们输入框的组件就是图片中的 validate-input,如果我们只需要在父组件中输入要验证的规则和验证失败的信息,把逻辑交给 validate-input 来判断,那整体代码就非常清晰了。

我们通过 rules 属性来传给组件指定验证类型。message字段是出现问题时提示的内容,因为我们的输入框组件可以使用不止一种规则,所以 RulesProp 应该是 RuleProp 的数组。如果以后要添加其他的规则,就可以直接在下面的 type 中添加,这样可扩展性非常高。

interface RuleProp {
 type: 'required' | 'email';
 message: string;
}
export type RulesProp = RuleProp[]

我们在子组件中定义规则的接口,然后定义都是这种类型的数组结构并把它导出出去方便父组件使用。如果不熟习 typescript 的朋友,就可以把它当作定义一个RuleProp对象,里面有两个属性,一个是规则类型,一个是出现问题时提示的内容。然后再定义一个对象数组,把它导出出去这样父组件向子组件传递的都是这种规定的对象数组。

子组件接受的 props把它断言成 RulesProp 类型的数组:

Vue自定义Form组件实现方法介绍

我们再看一下 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 类型的数组作为参数,传递给子组件:

Vue自定义Form组件实现方法介绍

现在启动项目,看一下效果:

Vue自定义Form组件实现方法介绍

Vue自定义Form组件实现方法介绍

这样我们这一节抽离验证规则的目的就达到了。

4. 支持 v-model 双向绑定

现在我们已经把验证规则抽离出来,实现了表单的基本验证,但是有一个痛点需要我们解决,我们在父组件中现在拿不到用户在输入框中输入的值,这样就实现不了下一步的其他需求,在 input 中我们通过 v-model 指令来进行双向绑定可以很轻松地获得用户输入的值,那么在 validate-input 组件中我们如何来实现 v-model 呢?

我们先看一下 vue3 中 v-model 的实现原理:

Vue自定义Form组件实现方法介绍

vue3 中摒弃了 vue2 里通过动态绑定 input 的 value 属性和 input 事件实现的双向绑定,通过 modelValue 这么个属性和 onUpdate:modelValue 这个事件来实现双向绑定。所以我们要实现 v-model 就要有上面的属性和更新输入框的时候出发的事件。

首先我们来写 validate-input 子组件中的内容,在 props 参数中加入 modelValue:

Vue自定义Form组件实现方法介绍

然后用 :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 ,看看效果:

Vue自定义Form组件实现方法介绍

可以看到我们成功实现了 v-model 的双向绑定:

Vue自定义Form组件实现方法介绍

5. 使用 $attrs 支持默认属性

在原生 input 中有很多属性,比如 placeholder ,如果我们在我们的输入框组件中添加这个属性,会正常显示在页面上吗?我们试一下:

Vue自定义Form组件实现方法介绍

启动项目,查看效果:

Vue自定义Form组件实现方法介绍

placeholder没有正常显示出来,我们查看控制台,看看哪里有问题:

Vue自定义Form组件实现方法介绍

可以看到我们的placeholder被直接添加到 input 的父级上了,那如何把属性正确添加在 input 上呢?

1. 首先我们只需要在组件的选项中设置 inheriAttrs: false:

Vue自定义Form组件实现方法介绍

2. 通过 $attrs 把属性添加到元素上:

我们先输出一下 $attrs 看看里面有什么:

Vue自定义Form组件实现方法介绍

Vue自定义Form组件实现方法介绍

这是一个响应式对象,里面包括了我们传递给子组件的属性

下面我们先通过 v-bind 绑定 $attrs :

Vue自定义Form组件实现方法介绍

给我们的 input 组件添加属性:

Vue自定义Form组件实现方法介绍

Vue自定义Form组件实现方法介绍

启动项目,查看输出:

Vue自定义Form组件实现方法介绍

现在成功给组件添加了 placeholder 属性,也成功添加了 type ,密码框也变成了小圆点。

6. 父组件调用子组件中的方法

现在我们要实现的就是点击提交按钮,然后分别进行两个输入框的验证。可是我们的验证方法在 validate-input 这个子组件中,所以我们就得在父组件中调用子组件里的方法来实现表单验证。

1. 给子组件添加 ref 属性:

Vue自定义Form组件实现方法介绍

Vue自定义Form组件实现方法介绍

2. 给提交按钮添加点击事件:

Vue自定义Form组件实现方法介绍

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组件实现方法介绍

到这里我们的表单组件就开发完成了。

来源:https://blog.csdn.net/qq_49900295/article/details/125294184

标签:Vue,自定义,Form,组件
0
投稿

猜你喜欢

  • MySQL索引优化之分页探索详细介绍

    2024-01-16 12:05:17
  • js实现无需数据库的县级以上联动行政区域下拉控件

    2024-01-17 01:05:53
  • ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'

    2024-01-15 21:45:27
  • openCV入门学习基础教程第三篇

    2022-05-20 00:00:59
  • pyinstaller打包多个py文件和去除cmd黑框的方法

    2022-12-18 13:52:30
  • 静态页面实现文章点击数统计的js方法

    2008-01-23 19:17:00
  • Pytorch中index_select() 函数的实现理解

    2023-11-26 16:24:32
  • 解决pycharm工程启动卡住没反应的问题

    2021-04-05 09:54:02
  • 在Python 中同一个类两个函数间变量的调用方法

    2022-07-02 04:53:57
  • 用Python输出一个杨辉三角的例子

    2023-06-04 07:32:33
  • mybatis如何实现的数据库排序

    2024-01-24 16:35:32
  • python 贪心算法的实现

    2023-01-25 08:18:50
  • Pytorch mask-rcnn 实现细节分享

    2021-10-20 01:31:38
  • MySQL选错索引的原因以及解决方案

    2024-01-19 21:34:34
  • 查找sqlserver查询死锁源头的方法 sqlserver死锁监控

    2024-01-23 01:10:46
  • 讲解Python的Scrapy爬虫框架使用代理进行采集的方法

    2022-07-28 01:34:49
  • 基于Django快速集成Echarts代码示例

    2021-12-10 18:53:32
  • Windows下Anaconda2安装NLTK教程

    2022-08-11 04:30:49
  • MATLAB数学建模之画图汇总

    2023-06-14 06:49:50
  • sql2000挂起无法安装的问题的解决方法

    2024-01-20 19:09:34
  • asp之家 网络编程 m.aspxhome.com