组件库实战|教你如何设计web世界中的表单验证(代码片段)

星期一研究室 星期一研究室     2023-02-23     414

关键词:

💬序言

在实际开发中,我们有一个很经常开发的场景,那就是登录注册。登录注册实际上涉及到的内容是表单验证,因此呢,表单验证也是 web 世界中一个很重要的功能。

那接下里就来了解,在实际的开发中,如何更规范合理地去开发一个表单验证,使其扩展性更强,逻辑更加清晰。

一起来学习⑧~

🗯️一、验证输入框ValidateInput

1. 设计稿抢先知

在了解具体的实现方式之前,我们首先来看原型图。看我们想要实现的表单是怎么样的。如下图所示:

大家可以看到,用我们最熟悉的表单验证就是登录注册操作。其中,整个表单包含四部分。

第一部分是红色框框的内容,红色框框想要做的事情就是,当元素失去焦点时候去触发事件。

第二部分是验证规则,我们不管是在输入用户名还是密码,都需要校验规则来进行校验,比如说不为空,限制输入长度等等内容。

第三部分是当验证没有通过时,需要出现具体的警告。

第四部分就是当所有内容都输入并且要进行提交时,要去验证整个 Form 表单。

2. 简单的实现

我们先来给表单进行一个简单的实现。现在我们在 vue3 项目中的 App.vue 下对整个表单先进行渲染,并且对邮箱的逻辑进行编写。具体代码如下:

<template>
  <div class="container">
    <global-header :user="user"></global-header>
    <form action="">
      <div class="mb-3">
        <label for="exampleInputEmail1" class="form-label">邮箱地址</label>
        <input
        type="email" class="form-control" id="exampleEmail1"
        v-model="emailRef.val"
        @blur="validateEmail">
        <div class="form-text" v-if="emailRef.error">emailRef.message</div>
      </div>
      <div class="mb-3">
        <label for="exampleInputPassword1" class="form-label">密码</label>
        <input type="password" class="form-control" id="exampleInputPassword1">
      </div>
    </form>
  </div>
</template>

<script lang="ts">
import  defineComponent, reactive, ref  from 'vue'
import 'bootstrap/dist/css/bootstrap.min.css'
import ColumnList,  ColumnProps  from './components/ColumnList.vue'
import GlobalHeader,  UserProps  from './components/GlobalHeader.vue'
const currentUser: UserProps = 
  isLogin: true,
  name: 'Monday'

// 判断是否是邮箱的格式
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`|~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/
const testData: ColumnProps[] = [
  
    id: 1,
    title: 'test1专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。'
    // avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  ,
  
    id: 2,
    title: 'test2专栏',
    description: '众所周知, js 是一门弱类型语言,并且规范较少。这就很容易导致在项目上线之前我们很难发现到它的错误,等到项目一上线,浑然不觉地,bug就UpUp了。于是,在过去的这两年,ts悄悄的崛起了。 本专栏将介绍关于ts的一些学习记录。',
    avatar: 'https://img0.baidu.com/it/u=3101694723,748884042&fm=26&fmt=auto&gp=0.jpg'
  
]

export default defineComponent(
  name: 'App',
  components: 
    GlobalHeader
  ,
  setup () 
	// 邮箱验证部分数据内容
    const emailRef = reactive(
      val: '',
      error: false,
      message: ''
    )
	// 验证邮箱逻辑
    const validateEmail = () => 
      // .trim 表示去掉两边空格
      // 当邮箱为空时
      if (emailRef.val.trim() === '') 
        emailRef.error = true
        emailRef.message = 'can not be empty'
       
       // 当邮箱不为空,但它不是有效的邮箱格式时
       else if (!emailReg.test(emailRef.val)) 
        emailRef.error = true
        emailRef.message = 'should be valid email'
      
    
    return 
      list: testData,
      user: currentUser,
      emailRef,
      validateEmail

    
  
)
</script>

现在,我们来看下具体的显示效果:

好了,现在我们第一步就实现啦!那么接下来,我们是不是就应该来写 password 的逻辑了呢?

但是啊,如果按照上面这种方式来写的话,有小伙伴会不会觉得就有点重复操作了呢。一两个校验规则还好,如果我们遇到十几二十个呢?也一样每一个都这么写吗?

答案当然是否定的。那么下一步,我们就要对这个校验规则,来进行抽象。

3. 抽象验证规则

继续,我们现在要来抽象出用户名和密码的校验规则,让其可扩展性更强。具体形式如下:

<validate-input :rules="" />

interface RuleProp 
    type: 'required' | 'email' | 'range' | ...;
    message: string;

export type RulesProp = RuleProp[]

首先,我们要先把表单组件给抽离出来。那么现在,我们在 vue3 项目下的 src|components 下创建一个文件,命名为 ValidateInput.vue其具体代码如下:

<template>
  <div class="validate-input-container pb-3">
      <!-- 手动处理更新和发送事件 -->
      <!-- 使用可选 class,用于动态计算类名 -->
      <input type="text"
        class="form-control"
        :class="'is-invalid': inputRef.error"
        v-model="inputRef.val"
        @blur="validateInput"
      >
      <span v-if="inputRef.error" class="invalid-feedback">inputRef.message</span>
  </div>
</template>

<script lang="ts">
import  defineComponent, reactive, PropType  from 'vue'
// 判断email的正则表达式
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`|~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/
// required表示必填值,email表示电子邮件的格式
// message用来展示当出现问题时提示的错误
interface RuleProp 
  type: 'required' | 'email';
  message: string;
  validator?: () => boolean;


export type RulesProp = RuleProp[]
export default defineComponent(
  name: 'ValidateInput',
  props: 
    // 用PropType来确定rules的类型,明确里面是RulesProp
    // 这里的rules数据将被父组件 App.vue 给进行动态绑定
    rules: Array as PropType<RulesProp>
  ,
  setup(props, context) 
    //   输入框的数据
    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
    
  
)
</script>

<style>

</style>

之后我们将其在 App.vue 下进行注册。具体代码如下:

<template>
  <div class="container">
    <global-header :user="user"></global-header>
    <form action="">
      <div class="mb-3">
        <label class="form-label">邮箱地址</label>
        <validate-input :rules="emailRules"></validate-input>
      </div>
      <div class="mb-3">
        <label for="exampleInputEmail1" class="form-label">邮箱地址</label>
        <input
        type="email" class="form-control" id="exampleEmail1"
        v-model="emailRef.val"
        @blur="validateEmail">
        <div class="form-text" v-if="emailRef.error">emailRef.message</div>
      </div>
      <div class="mb-3">
        <label for="exampleInputPassword1" class="form-label">密码</label>
        <input type="password" class="form-control" id="exampleInputPassword1">
      </div>
    </form>
  </div>
</template>

<script lang="ts">
import  defineComponent, reactive, ref  from 'vue'
import 'bootstrap/dist/css/bootstrap.min.css'
import ValidateInput,  RulesProp  from './components/ValidateInput.vue'
import GlobalHeader,  UserProps  from './components/GlobalHeader.vue'
const currentUser: UserProps = 
  isLogin: true,
  name: 'Monday'

// 判断是否是邮箱的格式
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`|~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/

export default defineComponent(
  name: 'App',
  components: 
    GlobalHeader,
    ValidateInput
  ,
  setup () 
    const emailRules: RulesProp = [
       type: 'required', message: '电子邮箱不能为空' ,
       type: 'email', message: '请输入正确的电子邮箱格式' 
    ]
    const emailRef = reactive(
      val: '',
      error: false,
      message: ''
    )
    const validateEmail = () => 
      if (emailRef.val.trim() === '') 
        emailRef.error = true
        emailRef.message = 'can not be empty'
       else if (!emailReg.test(emailRef.val)) 
        emailRef.error = true
        emailRef.message = 'should be valid email'
      
    
    return 
      user: currentUser,
      emailRef,
      validateEmail,
      emailRules
    
  
)
</script>

现在,我们在浏览器来看下它好不好用。具体效果如下:

大家可以看到,经过抽离后的验证规则,也正确的显示了最终的验证效果。课后呢,大家可以继续对 RuleProptype 进行扩展,比如多多加一个 range 功能等等。

到了这一步,我们对验证规则已经进行了简单的抽离。那接下来要做的事情就是,让父组件 App.vue 可以获取到子组件 ValidateInput.vueinput 框的值,对其进行数据绑定。

4. v-model

说到 input ,大家首先想到的可能是 v-model 。我们先来看下 vue2vue3 在双向绑定方面的区别:

<!-- vue2 原生组件 -->
<input v-model="val">
<input :value="val" @input="val = $event.target.value">

<!-- vue2自定义组件 -->
<my-component v-model="val" />
<my-component :value="val" @input="val = argument[0]" />

<!-- 非同寻常的表单元素 -->
<input type="checkbox" checked="val" @change="">

<!-- vue3 compile 以后的结果 -->
<my-component v-model="foo" />
h(Comp, 
	modelValue: foo,
	'onUpdate: modelValue': value => (foo = value)
)

对于 vue2 的双向绑定来说,主要有以下槽点:

  • 比较繁琐,需要新建一个 model 属性;
  • 不管如何,都只能支持一个 v-model ,没办法双向绑定多个值;
  • 写法比较让人难以理解。

基于以上 vue2 的几个槽点,现在我们用 vue3 来对这个组件的 input 值进行绑定,手动对其处理更新和事件发送。

首先我们在子组件 ValidateInput.vue 中进行处理,处理数据更新和事件发送。具体代码如下:

<template>
  <div class="validate-input-container pb-3">
      <input type="text"
        class="form-control"
        :class="'is-invalid': inputRef.error"
        :value="inputRef.val"
        @blur="validateInput"
        @input="updateValue"
      >
      <span v-if="inputRef.error" class="invalid-feedback">inputRef.message</span>
  </div>
</template>

<script lang="ts">
import  defineComponent, reactive, PropType  from 'vue'
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`|~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/
interface RuleProp 
  type: 'required' | 'email';
  message: string;
  validator?: () => boolean;


export type RulesProp = RuleProp[]
export default defineComponent(
  name: 'ValidateInput',
  props: 
    rules: Array as PropType<RulesProp>,
    // 创建一个字符串类型的属性 modelValue
    modelValue: String
  ,
  setup(props, context) 
    // 输入框的数据
    const inputRef = reactive(
      val: props.modelValue || '',
      error: false,
      message: ''
    )
    // KeyboardEvent 即键盘输入事件
    const updateValue = (e: KeyboardEvent) => 
      const targetValue = (e.target as HTMLInputElement).value
      inputRef.val = targetValue
      // 更新值时需要发送事件 update:modelValue
      context.emit('update:modelValue', targetValue)
    
    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)
              breakvue项目实战11绘制登录组件-数据验证篇(代码片段)

接上篇《10、绘制登录组件-表单篇》上一篇我们绘制了登录组件的表单部分,包括账号密码的填写框,以及登录和重置的按钮,并通过数据绑定获取到账号密码数据。本篇我们讲解如何实现账号密码的数据验证、表单... 查看详情

如何搭建组件库(基础实战篇)

一、搭建组件库有什么好处?让设计更高效、开发更迅速、产品体验更一致。很多大厂也做了自己的组件库,比如AntDesign,Element等,一个成熟的组件库确实让产品的体验更好,团队的效率更高。如果你也想开始搭建你们产品自... 查看详情

如何搭建组件库(基础实战篇)

一、搭建组件库有什么好处?让设计更高效、开发更迅速、产品体验更一致。很多大厂也做了自己的组件库,比如AntDesign,Element等,一个成熟的组件库确实让产品的体验更好,团队的效率更高。如果你也想开始搭建你们产品自... 查看详情

模板驱动表单中的表单验证问题

...使用Angular6开发一个Web应用程序。我想创建一些受HTML输入组件启发的自定义组件。例如:CustomComponent.ts(打字稿)@Component(selector:\'custom-component\',templ 查看详情

Vuelidate:使用子组件验证表单

】Vuelidate:使用子组件验证表单【英文标题】:Vuelidate:validateformwithsubcomponents【发布时间】:2019-06-1800:49:36【问题描述】:如何使用Vuelidate验证父组件内的嵌套组件?如果子组件中的输入有效与否,我想更改parentForm.$invalid。家... 查看详情

finereport怎么进行表单设计finereport的表单设计简单示例

...佼佼者。新建表单点击文件>新建表单,如下图:拖入组件如上图所示的效果图,我们可以看到该表单需要有1个下拉框控件,一个文本控件,以及对应的2个标签控件,还需要一个以表格形式显示数据的报表块,此时,我们确定... 查看详情

尝试在 Vanilla JS 中创建 Web 组件库,你会如何开始?

】尝试在VanillaJS中创建Web组件库,你会如何开始?【英文标题】:TryingtocreateawebcomponentLibraryinVanillaJS,Howwouldyoustart?【发布时间】:2020-12-2718:21:47【问题描述】:我是一个刚接触编程的女孩,我想在vanillajs中创建一个小型可构建库... 查看详情

element-ui表单组件的prop属性

参考技术AVue组件库element-ui中的Form表单组件提供了表单验证功能通过rules属性传入验证规则Form-Item中的prop属性设置需要校验的字段名如图所示,el-form-item元素的prop属性绑定字段名username,表单验证时,就会验证el-input元素绑定的... 查看详情

flaskwtforms组件详解(代码片段)

...单验证插件就是wtforms。wtfroms是一个支持多种web框架的form组件,主要用于对用户请求数据的进行验证,其的验证流程与django中的form表单验证由些许类似,本文将介绍wtforms组件使用方法以及验证流程。  wtforms依照功能类别来说... 查看详情

vue项目实战12登录与退出功能-请求登录(代码片段)

接上篇《11、绘制登录组件-数据验证篇》上一篇我们讲解了如何实现账号密码的数据验证、表单的重置以及登录前的预验证。本篇我们来讲解点击登录后发起登录请求的操作。一、进行登录请求上一篇我们在点击登录按钮前校验... 查看详情

js实战·表单验证

思路:    1、定义页面      通过表格格式化表单;      表格行都有自己的背景颜色;      单元格中的数据(文本等)用div进行封装,好操作; &nb... 查看详情

十大常用web前端ui组件库,赶紧收藏

...要多多学习使用。Vant一款有赞出品轻量、可靠的的移动UI组件库,目前支持Vue2、Vue3、React,微信和支付宝小程序,并由社区团队维护React版本和支付宝小程序版本。官网的文档清晰易懂,如果你熟悉vue.js的组件化... 查看详情

如何从 Angular 1.5 组件中获取表单验证

】如何从Angular1.5组件中获取表单验证【英文标题】:howtogetformvalidationfromangular1.5component【发布时间】:2017-07-1612:40:27【问题描述】:我知道如何从Angular1.5设置表单验证例如:$scope.voiceForm.$setValidity但是我如何才能get来自组件代... 查看详情

如何在 Laravel 刀片中的反应组件中获取 Laravel 验证错误

】如何在Laravel刀片中的反应组件中获取Laravel验证错误【英文标题】:HowtogetLaravelvalidationerrorsinreactcomponentwhichisinLaravelblade【发布时间】:2020-03-2514:42:38【问题描述】:我是React新手。我在Laravel刀片中使用了反应表单组件部分。... 查看详情

深入浅出java并发编程指南「实战篇」教你如何使用abstractqueuedsynchronizer实现自己的同步器组件(代码片段)

前提概要之前的文章中会涉及到了相关AQS的原理和相关源码的分析,所谓实践是检验真理的唯一标准!接下来就让我们活化一下AQS技术,主要针对于自己动手实现一个AQS同步器。定义MyLock实现LockDougLea大神在JDK1.5编写... 查看详情

表单设计器中的组件数组

】表单设计器中的组件数组【英文标题】:ArrayofComponentsinFormsDesigner【发布时间】:2012-05-0918:57:57【问题描述】:在Windows窗体设计器中是否有任何好的方法来定义类似组件的数组(或其他集合)?您可以检查“GenerateMember”,它... 查看详情

手把手教你跑larave框架实战笔记系列之一

[宗旨]严格遵循现代编程组件化Web开发原理,通过phpRE+Composer+PhpStorm+Laravel快捷安装配置集成强大现代优雅开发平台,实现一站式Web开发。[前言]据调查显示,目前45-54岁之间的开发者,超过一半在16岁之前就开始编程,18-24... 查看详情

将表单传递给 AngularJS 组件进行验证

】将表单传递给AngularJS组件进行验证【英文标题】:PassingformtoAngularJScomponentforvalidation【发布时间】:2016-08-2703:14:57【问题描述】:我正在将我的旧代码库迁移到使用AngularJS1.5推广的新组件架构中。我在为较大的表单执行此操作... 查看详情