Validate表单组件的封装

news2024/10/9 18:20:13

在这里插入图片描述

之前一直是直接去使用别人现成的组件库,也没有具体去了解人家的组件是怎么封装的,造轮子才会更好地提高自己,所以尝试开始从封装Form表单组件开始

一:组件需求分析

本次封装组件,主要是摸索封装组件的流程,对于具体需要的方法和属性,只会实现其中部分方法和属性,之后一点一点才进行添加

image-20230909204141276

  • 表单项组件,ValidateInput组件的封装
    • 根据传递不同的type类型有着不同的校验规则
    • 支持V-model,对于封装的自定义组件支持v-model双向绑定也是一个很关键的属性
    • tag根节点的类型
  • form组件,提交事件
    • 验证整体表单项,是否通过
    • 清空表单项的内容

二:技术栈

  • Vue3
  • TS
  • Bootstrap样式库

三:封装Validate-input验证表单项组件

思路

1. 明确属性和事件

  • v-model属性
  • rules属性:根据不同type不同校验规则
  • tag根节点类型

ValidateInput组件

<template>
  <div class="validate-input-container pb-3">
    <input
      class="form-control"
      :class="{ 'is-invalid': inputRef.error }"
      :value="modelValue"
      @blur="validateInput"
      @input="updateValue"
      v-bind="$attrs"
    />
    <span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span>
  </div>
</template>
<script setup lang="ts">
//禁用 Attributes 继承
defineOptions({
  inheritAttrs: false
})
//定义传来的一个参数类型
interface RangeProp {
  message: string
  length: number
}
interface RuleProp {
  type: 'required' | 'email' | 'range'
  message?: string
  //至少位数
  min?: RangeProp
  max?: RangeProp
}
//数组类型
export type RulesProp = RuleProp[]
//接收参数
const props = defineProps<{
  rules?: RulesProp
  //自定义组件使用v-model需要用modelValue来接收参数
  modelValue: string
}>()

//定义表单的数据
const inputRef = reactive({
  //如果为空
  val: props.modelValue || '',
  error: false, //表单验证是否通过
  message: '' //错误信息
})
</script>
  • 禁用Attributes继承:透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

    <!-- <MyButton> 的模板 -->
    <button>click me</button>
    

    当父组件使用该组件,并且传入class:

    <MyButton class="large" />
    

    最终会在根元素出现class=‘large“,<MyButton> 并没有将 class 声明为一个它所接受的 prop,所以 class 被视作透传 attribute,自动透传到了 <MyButton> 的根元素上。

    因此需要禁用Attibutes继承

  • 定义接收参数类型

    • rules可选参数,接收数组
    • modelValue:接收字符串即输入的值

2. v-model属性

在使用一个自定义组件给其添加v-model属性,其自定义组件内部做了两件事

  • 将内部原生的input元素的值绑定到组件内部Prop属性modelValue
  • 当原生input元素触发时候,触发一个携带了新值的 update:modelValue 自定义事件
          <!-- 自定义组件Validate-input -->
          <Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="请输入邮箱地址"></Validate-input>

Validate-input组件

//接收参数
const props = defineProps<{
  rules?: RulesProp
  //自定义组件使用v-model需要用modelValue来接收参数
  modelValue: string
}>()

props接收了,modelValue属性,类型string

    <input
      class="form-control"
      :class="{ 'is-invalid': inputRef.error }"
      :value="modelValue"
      @blur="validateInput"
      @input="updateValue"
      v-bind="$attrs"
    />

设置了自定义事件用于更新value

//手动更新value
const updateValue = (e: Event) => {
  //HTMLInputElement输入元素类型
  let targetValue = (e.target as HTMLInputElement).value
  //更新文本框的值
  inputRef.val = targetValue
  emit('update:modelValue', targetValue)
}

因此实现v-model属性

3. rules属性

  • 接收参数
    • type:校验类型,requied | email | range
    • message
    • min:至少几位
    • max:至多几位

抽象验证逻辑

validate-input组件

//数组类型
export type RulesProp = RuleProp[]
//接收参数
const props = defineProps<{
  rules?: RulesProp
  //自定义组件使用v-model需要用modelValue来接收参数
  modelValue: string
}>()

参数接收rules是个数组

//定义表单的数据
const inputRef = reactive({
  //如果为空
  val: props.modelValue || '',
  error: false, //表单验证是否通过
  message: '' //错误信息
})

定义表单的数据

//定义事件
const validateInput = () => {
  //定义邮箱的正则
  let emailReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/

  //如果传过来的有验证的话
  if (props.rules) {
    //every方法  遍历全部数组只有全部满足条件才会返回true, 只要有一个false停止遍历
    let allRulePassed = props.rules.every(rule => {
      let passed = true
      //消息赋值 类型断言
      inputRef.message = rule.message as string
      //switch选择对应type类型错误进行校验
      switch (rule.type) {
        case 'required':
          if (inputRef.val.trim() === '') {
            passed = false
          }
          break

        case 'email':
          if (!emailReg.test(inputRef.val)) {
            passed = false
          }
          break
        case 'range':
          passed = validateRange(rule.min, rule.max)
          break
      }
      return passed
    })
    //allRulePassed为false表示通过
    //所以Input.error表示错误应该为true
    inputRef.error = !allRulePassed
    return allRulePassed
  }
  return true
}

定义校验事件

  • 满足全部rules校验才可以通过,因此用到es6数组方法,every只有全部项都为true才会遍历全部返回true,只要有一个项结果为false,就会停止遍历

  • message校验信息赋值给inputRef.message

  • 利用swicth case语句,选择对应的type类型进行校验

  • 需要充分考虑到所有情况,通过passed为true,未通过为fasle

  • range长度校验,由于情况较多,单独封装一个函数去校验

    • //校验长度
      const validateRange = (min: RangeProp | undefined, max: RangeProp | undefined) => {
        let passed = true
        //1. 如果min 存在 ,max不存在
        if (min && !max) {
          inputRef.message = min.message
          passed = !(inputRef.val.length < min.length)
        }
        //2. min不在, max在
        if (!min && max) {
          inputRef.message = max.message
          passed = !(inputRef.val.length > max.length)
        }
        //3. min在 max在
        if (min && max) {
          //若小于
          if (inputRef.val.length < min.length) {
            passed = false
            inputRef.message = min.message
          }
          //若大于
          if (inputRef.val.length > max.length) {
            passed = false
            inputRef.message = max.message
          }
        }
        return passed
      }
      

      image-20230909212538049

  • 模板中,根据passed的值,动态绑定未通过的样式

4. 默认属性

validate-input组件

  <div class="validate-input-container pb-3">
    <input
      class="form-control"
      :class="{ 'is-invalid': inputRef.error }"
      :value="modelValue"
      @blur="validateInput"
      @input="updateValue"
      v-bind="$attrs"
    />
    <span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span>
  </div>

父组件

          <Validate-input
            v-model="loginParams.password"
            :rules="passwordRules"
            type="password"
            placeholder="请输入密码"
          ></Validate-input>
  • 通过显示绑定attrs,使得自定义组件可以使用默认属性

  • ValidateInput组件中,可以通过$attrs属性将type属性将被传递到组件的根元素上

  • 可以在父组件中灵活地传递任何HTML属性给ValidateInput组件,使它更加通用和可配置

四:封装Validate-Form组件

  • 事件
    • form-submit:点击提交触发的事件,回调参数result是布尔值,表示该表单是否通过了校验

1. form-submit事件

  • 当点击提交时,要去验证该表单是否通过了校验,就需要一个个将表单项进行校验,只要一个没通过就返回false,只有全部通过返回true

收集全部表单项校验函数

  • 可以通过将所有表单项的校验函数都添加到一个数组中,然后最终通过every方法遍历是否全部通过

收集

安装mitt并使用,监听事件

npm install mitt --save

全局挂载

main.ts

import mitt from 'mitt'
//对外暴露全局事件总线实例
export const bus = mitt()

ValidateForm组件中使用

<script setup lang="ts">
import { onUnmounted, reactive } from 'vue'
import { bus } from '@/main'
const emits = defineEmits(['form-submit'])
//点击事件
//定义函数类型
type ValidateFunc = () => boolean
//定义类型
//定义接收的函数数组
const funcArr = reactive<ValidateFunc[]>([])
//测试回调
const callback = (func: ValidateFunc) => {
  //将每个校验函数加入数组
  funcArr.push(func)
}
//订阅事件
bus.on('form-item-created', callback as ValidateFunc)

//提交按钮触发事件
const submitForm = () => {
  const result = funcArr.map(func => func()).every(result => result)

  // 触发提交事件
  //遍历funcArr中的每个校验函数
  emits('form-submit', result)
}
onUnmounted(() => {
  // 移除订阅
  bus.off('form-item-created', callback as ValidateFunc)
})
</script>
  • 定义函数类型,返回值为空
  • 利用泛型定义存放每个表单项校验函数存放的数组
  • 订阅事件,在组件卸载的时候再取消订阅

ValidateInput组件中

  • 组件挂载的时候,就直接触发自定义事件,然后将每一项的校验函数传递给ValidateForm组件

Validate-Form组件

  • 然后接受ValidateInput组件传递过来的校验函数,一个个加入到数组中
  • 在submitForm事件中,遍历数组,不能直接使用every方法,因为只要一个不通过就不进行后面的校验,这是不满足我们的需求的
    • 可以看到下面的错误实例,密码原本也是不通过校验的,但是every方法直接在第一个校验失败结束遍历了

image-20230909220625051

  • 因此先利用map函数,先使得每个校验函数都执行,结束后返回一个新的数组存放校验函数的返回值,再通过every遍历数组
  • 最后触发自定义事件,将结果传递给父组件中

父组件

<Validate-Form @form-submit="submitForm" ref="validateFormRef">
const submitForm = (result: boolean) => {
  console.log(result)
}

提交表单元素后清空表单值

具体逻辑跟校验差不多

Validate-Form组件

<script setup lang="ts">
import { onUnmounted, reactive } from 'vue'
import { bus } from '@/main'
const emits = defineEmits(['form-submit'])
//点击事件
//定义函数类型
type ValidateFunc = () => boolean
//定义清空Input函数类型
type clearInputsFunc = () => void
//定义类型
//定义接收的函数数组
const funcArr = reactive<ValidateFunc[]>([])
//定义接收用于清空的函数数组
const clearFuncArr = reactive<clearInputsFunc[]>([])
//测试回调
const callback = (func: ValidateFunc) => {
  //将每个校验函数加入数组
  funcArr.push(func)
}
//事件回调
const clearInputFunc = (func: clearInputsFunc) => {
  clearFuncArr.push(func)
}
//绑定监听事件
bus.on('form-item-created', callback as ValidateFunc)
bus.on('form-item-clear', clearInputFunc as clearInputsFunc)

//提交按钮触发事件
const submitForm = () => {
  const result = funcArr.map(func => func()).every(result => result)

  // 触发提交事件
  //遍历funcArr中的每个校验函数
  emits('form-submit', result)
  //遍历清空函数数组并依次并执行
  //当校验通过时候才会清空input
  if (result) {
    clearFuncArr.map(func => func())
  }
}
onUnmounted(() => {
  // 移除事件监听器
  bus.off('form-item-created', callback as ValidateFunc)
  bus.off('form-item-clear', clearInputFunc as clearInputsFunc)
})
</script>

Validate-input组件


onMounted(() => {
  //直接把validateInput校验事件传递过去
  bus.emit('form-item-created', validateInput)
  //直接触发事件先传入每个input的值
  bus.emit('form-item-clear', clearInput)
})

//定义表单的数据
const inputRef = reactive({
  //如果为空
  val: props.modelValue || '',
  error: false, //表单验证是否通过
  message: '' //错误信息
})
  • 把清空表单数据的处理函数收集起来
  • 最后利用map方法依次执行清除即可

五:演示和使用

image-20230909221802127

在 vue template 中添加结构代码

      <Validate-Form @form-submit="submitForm" ref="validateFormRef">
        <!-- 邮箱地址 -->
        <div class="mb-3">
          <label for="exampleInputEmail1" class="form-label">邮箱地址</label>
          <!-- 自定义组件Validate-input -->
          <Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="请输入邮箱地址"></Validate-input>
        </div>
        <!-- 密码 -->
        <div class="mb-3">
          <label for="exampleInputPassword1" class="form-label">密码</label>
          <Validate-input
            v-model="loginParams.password"
            :rules="passwordRules"
            type="password"
            placeholder="请输入密码"
          ></Validate-input>
        </div>
        <template #submit>
          <button type="submit" class="btn btn-primary btn-block btn-large">提交</button>
        </template>
      </Validate-Form>

在 setup语法糖中添加数据

//定义验证类型数据
const emailRules: RulesProp = [
  { type: 'required', message: '电子邮箱地址不能为空' },
  { type: 'email', message: '请输入正确的电子邮箱格式' }
]
//定义密码验证类型数据
const passwordRules: RulesProp = [
  { type: 'required', message: '密码不能为空' },
  {
    type: 'range',
    min: { message: '你的密码至少包括6位,不能含有空格', length: 6 },
    max: {
      message: '你的密码至多15位,不能含有空格',
      length: 15
    }
  }
]

//监听结果
const submitForm = (result: boolean) => {
  console.log(result)
}

Validate-Form属性和事件

  • form-submit
    • 类型:事件
    • 默认:-
    • 说明:回调参数 (result: boolean) => void, result 代表是否通过了验证

Validate-Input属性和事件

  • rules
    • 类型:array
    • 默认:-
    • 说明: 单个输入框的验证类型,可以传入包含特定对象的数组, 详情可见上面示例代码
  • tag
    • 类型:input | textarea
    • 默认:input
    • 说明: 根节点类型
  • v-model
    • 类型: string
    • 默认:-
    • 说明: 支持 v-model,请对响应式数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/994041.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哪个mac虚拟机软件好?怎么选择

虚拟机软件可以说是部分苹果用户们都会使用到的&#xff0c;因为很多软件在Mac上并不兼容&#xff0c;大部分都是基于Windows的框架进行开发设计的。虽然也有出Mac版本&#xff0c;但往往推迟得比较久才会进行发布。 拥有了虚拟机软件之后&#xff0c;我们就能够虚拟想要的系统…

lenovo联想笔记本ThinkPad P16V Gen 1(21FC,21FD)原装出厂Win11系统

原厂W11系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 链接&#xff1a;https://pan.baidu.com/s/17dTExDSz-EDN4Qd-PZGJuw?pwdrgl3 提取码&#xff1a;rgl3 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;ISO 文件大小…

油猴插件(Tampermonkey)的使用教程

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 “油猴插件” 与 “油猴扩展程序” 表示同一个意思&#xff0c;下面统一使用“油猴插件”这个名词。 油猴插件的简介 浏览器插件&#xff0c;包括油猴插件和其他插件&#xff0c;通过它们可以实现浏览器网…

Vue 路由守卫详细介绍与演示

Vue 路由守卫是一种在 Vue.js 应用程序中控制路由导航的机制&#xff0c;它允许你在路由变化前、后或在特定路由上执行代码&#xff0c;以便实现诸如权限控制、数据加载、页面切换动画等功能。在下面的介绍中&#xff0c;我将首先提供官方定义和通俗解释&#xff0c;然后详细介…

rosbag 包转TUM数据集

参考链接&#xff1a; ROS学习&#xff1a;制作自己的TUM数据集 配置环境 1.安装ROS 参考我的博客 https://blog.csdn.net/qin_liang/article/details/127035615 2.查看rosbag中的topic rosbag info xxx.bag3.创建catkin_ws/src文件夹 在src下运行 catkin_create_pkg rosb…

<OpenCV> Mat属性

OpenCV的图像数据类型可参考之前的博客&#xff1a;https://blog.csdn.net/thisiszdy/article/details/120238017 OpenCV-Mat类型的部分属性如下&#xff1a; size&#xff1a;矩阵的大小&#xff0c; s i z e ( c o l s , r o w s ) size(cols,rows) size(cols,rows)&#xf…

如何维持股市稳定?——股市定海神针

中国股市于1989年 开始&#xff0c;至今2023年&#xff0c; 已有30多个年头。而这30多年来&#xff0c;却有20多年钟情于3000点。 股市有赌性在&#xff0c;却也为数以千计的企业提供了养料&#xff0c;更关系着数以亿计的股民、以及企业员工的切身利益。 股市3000点&#xff…

(翻译)JavaFX高级教程:JavaFX2.0的FXML语言

原文地址http://download.oracle.com/javafx/2.0/fxml_get_started/jfxpub-fxml_get_started.htm FXML是JavaFX 2.0新引入的。你可能会问"What is FXML?" 和"Is FXML for me?" FXML 是基于XML的一种声明性标记语言&#xff0c;用来定义应用的用户接口。F…

区间DP 计数类DP 数位统计DP 状态压缩DP 树形DP 记忆化搜索

目录 区间DP石子合并分析思路代码实现 计数类DP整数划分完全背包DP的解法二维数组实现一维优化实现 另类DP状态表示的解法&#xff08;分拆数&#xff09;二维数组实现一维优化实现 数位统计DP计数问题注意代码实现 状态压缩DP蒙德里安的梦想实现思路朴素实现预处理优化实现 最…

【视频图像篇】FastStone Capture屏幕长截图软件

【视频图像篇】FastStone Capture屏幕长截图软件 FastStone Capture最常用的一款屏幕长截图软件—【蘇小沐】 文章目录 【视频图像篇】FastStone Capture屏幕长截图软件实验环境1、启动界面2、自定义工具栏3、自动保存 &#xff08;一&#xff09;长截图1、捕获滚动窗口2、捕获…

LINUX 文件基本管理

一、文件类型和根目录结构 1、文件类型 可以通过 ls -l 或者 ll来查看文件类型 可以根据显示&#xff0c;查看第一个字符&#xff0c;就表示文件类型。 - 字符&#xff1a;普通文件&#xff0c;类似于Windows的记事本。 d 字符&#xff1a;目录文件&#xff0c;类似于Wind…

认识异常【超详细】

文章目录 1. 异常的概念与体系结构1.1 异常的概念1.2 异常的体系结构1.3 异常的分类1. 编译时异常2. 运行时异常 2. 异常的处理2.1 防御式编程2.2 异常的抛出2.3 异常的捕获2.3.1 异常声明throws2.3.2 try-catch捕获并处理2.3.3 finally 2.4 异常的处理流程 3. 自定义异常类 1.…

常见缺少msvcp140.dll问题及解决方法,分享多种方法帮你解决

在日常使用电脑的过程中&#xff0c;我们可能会遇到各种问题&#xff0c;比如电脑提示msvcp140.dll文件丢失。这个问题通常是由于某些程序或游戏需要这个dll文件来正常运行&#xff0c;但是由于某种原因&#xff0c;这个文件被误删或者损坏了。那么&#xff0c;如何解决这个问题…

FFmpeg入门之简单介绍

FFmpeg是什么意思: Fast Forward Moving Picture Experts Group ffmpeg相关文档: Documentation FFmpeg ffmpeg源码下载: https://git.videolan.org/git/ffmpeg.git https://github.com/FFmpeg/FFmpeg.git FFmpeg能做什么? 多种媒体格式的封装与解封装 : 1.多种音…

五)Stable Diffussion使用教程:文生图之高清修复

上一篇我们说到图生图,这一篇来说说高清修复。 上一篇我们通过一个例子实现了图生图的功能,使用一张图片生成了另一种风格的图片。 然而,我们生成的图片质量不尽如人意。 虽然我们之前也提到设置分辨率、精炼提示词去提升画面质量等等,但是实际用下来发现,分辨率拉得太…

UMA 2 - Unity Multipurpose Avatar☀️六.Advanced Occlusion高级遮挡功能解决皮肤服饰穿模

文章目录 🟥 本节功能效果展示🟧 基础项目配置🟨 本节项目配置🟩 配置MeshHideAsset1️⃣ 创建MeshHideAsset2️⃣ 配置SlotDataAsset3️⃣ 配置遮挡信息🟦 将 MeshHideAsset 配置到 Recipe🟥 本节功能效果展示 未遮挡前的穿模问题: 遮挡后效果:

【以太网通信】MDIO 管理接口及控制器设计

MDIO 管理接口是以太网 MAC 和 PHY 之间的接口&#xff0c;用于管理/配置以太网 PHY 芯片。本文主要介绍 MDIO 管理接口定义&#xff0c;以及 MDIO 控制器设计。 目录 1 MDIO 管理接口 2 MDIO 控制器设计 1 MDIO 管理接口 MDIO 管理接口是以太网 MAC 和 PHY 之间的接口&#…

VS2010 Windows API 串口编程 (二)

目录 一串口通信基础 1.1串口通信原理与特点 1.2串口通信的传输方式 1.3串口通信的同步技术 1.4串行接口标准 二 API函数实现串口通信 2.1打开串口 2.1.1串口是否有驱动 2.1.2连接串口 2.1.3串口逻辑端口号大于10无法打开问题…

TCP原理(全网最详细)

一、确认应答&#xff08;可靠性机制&#xff09; TCP诞生的初衷就是可靠传输 可靠传输是TCP最核心的部分&#xff0c;TCP内部很多机制都是在保证可靠传输&#xff08;可以理解为发一条消息&#xff0c;上面显示已读未读&#xff0c;可靠传输就是发一条消息我知道对方是否收到…

【wordPress】WordPress删除index.php后缀【亲测有效】(手把手教学)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…