亲宝软件园·资讯

展开

Vue自定义Form组件

YinJie… 人气:0

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. 启动项目,查看效果:

直接点击提交按钮:

输入错误邮箱格式,点击提交:

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

加载全部内容

相关教程
猜你喜欢
用户评论