VUE中使用TypeScript装饰器实现表单验证的全过程

目录
  • 前言
  • 装饰器
  • class-validator
  • 封装Validator
  • 具体使用
  • 小结

前言

最近接触了关于很多TypeScript装饰器的知识,以及class-validator这个用装饰器来做表单验证的包,就萌生了想在vue中使用装饰器来做表单验证的想法。class-validator允许我们在类上通过使用装饰器来完成表单的验证,并且可在浏览器端和node端同时使用。那么接下来先简单介绍一下装饰器和class-validator的用法。

装饰器

装饰器的语法十分简单,只需要在想使用的装饰器前加上@符号,装饰器就会被应用到目标上。 通过装饰器我们可以轻松实现代理模式来使代码更简洁以及实现其它一些更有趣的能力。 关于装饰器的用法我用代码来简单的举例几个,更详细的信息大家可自行去网络上查找。

// 比如我们有一个创建用户的表单,在上面已经应用了一个Reactive类装饰器
// 用了这个装饰器后,这个类实例出来的对象会是响应式对象了。
@Reactive()
export class CreateUserForm {
  username:string
  email:string
  password:string
  confirmPassword:string
}

之后我们在setup中使用它

setup() {
  // 如果没有用装饰器,则需要 const form = reactive(new CreateUserForm())
  const form = new CreateUserForm()
  return {
    form
  }
}

那么这个类装饰器是怎么写的呢,其实很简单

import {reactive} from 'vue-demi'
// vue-demi可以让你的库同时在vue2(@vue/composition-api)和vue3中使用
//下面这个函数Reactive就是类装饰器,返回的是继承之后的类。实例化之后返回的是reactive对象
function Reactive() {
  return function <T extends { new (...args: any[]): {} }>(constructor: T){
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args)
        return reactive(this)
      }
    }
  }
}

class-validator

然后我们通过class-validator(github.com/typestack/c… 这个库给我们的表单加上表单验证

import { IsEmail, IsMobilePhone, Length } from 'class-validator'
@Reactive()
export class CreateUserForm {
    // 下面这些是属性装饰器,用来标记这些属性的验条件。
    // 在验证的时候会通过Reflect拿到这些元数据来验证
    // 我们也可以创建自定义的装饰器
    @Length(4, 12)
    username: string

    @IsEmail()
    email: string

    @IsMobilePhone('zh-CN')
    phone: string

    @Length(4, 12)
    password: string
}

之后在setup中使用,但还是显得有点粗糙

import { validate } from 'class-validator'
setup() {
  const form = new CreateUserForm()
  const errors = reactive< { [x in keyof CreateUserForm]: string} >({})
  const validate = async () => {
    const err = await validate(form)
    err.forEach(e => {
      if (e.constraints) {
        errors[e.property] = Object.values(e.constraints)[0]
      }
    })
  }
  return {
    form,
    errors
  }
}

// 用的是jsx,看个人习惯
render(){
  const {form,errors} = this
  return <div>
            <div>
                <p>
                    <span>用户名</span>
                    <input v-model={form.username}></input>
		</p>
                {!!errors.username && <p>错误提示:{errors.username}</p>}
            </div>
            // ...一些其他表单
            <button onClick={() => this.validate()}>验证</button>
        </div>
}

这里会有一些需要优化的地方

  • 在用的时候每次需要声明errors和validate方法,不方便
  • 需要手动点击验证才会有表单验证
  • 在输入表单的时候没有响应式的显示当前字段的错误提示

封装Validator

有许多种方法可以优化这个,这里我选择封装一个Validator类,有获取错误消息和验证的功能,然后让我们的表单类继承它。

import { instanceToPlain } from 'class-transformer'
import { validate, ValidationError } from 'class-validator'
import { toRef, watch } from 'vue-demi'
const ERROR = Symbol('error')
const IS_VALID = Symbol('isValid')
// 本来我打算error的类型简单的写成Record<string,any>
// 但是代码提示太不友好了,写成这样的话,可以完美的提示
// 这个接口写起来很麻烦
type ValidatorError < T > = {
    [x in Exclude < keyof T, keyof Validator >] ?:
    T[x] extends PropertyKey ? string: ValidatorRequiredError < T[x] >
}
type ValidatorRequiredError < T > = {
    [x in Exclude < keyof T, keyof Validator > ] :
        T[x] extends PropertyKey ?
        string | undefined:
        T[x] extends Function ?
        T[x] :
        ValidatorError < T[x] > |undefined
}
type ValidatorJSON < T > = {
    [x in Exclude < keyof T, keyof Validator > ] :
    keyof T extends PropertyKey ? T[x] : ValidatorJSON < T[x] >
}

export default abstract class Validator {
    // 这里属性用symbol是为了防止跟表单属性重复
    private [ERROR]: ValidatorError <this> ={}
    private [IS_VALID] : boolean = false

    public getError() {
      return this[ERROR]
    }
    public isValid() {
      return this[IS_VALID]
    }
    public toJSON() {
      return instanceToPlain(this) as ValidatorJSON < this >
    }
    public async validate() {
        // 一些验证的代码
    }
    public clearError() {
      this[ERROR] = {}
    }
    private setError(result: ValidationError[]):Record <string,any > {
       // 将error设置到this[ERROR]上
    }
    private watchFields(parentKeys ? :string[]) {
       // 这里做了单独watch每个属性,然后单独设置错误消息
    }
}
    -----------------------------------------
//上面watchFields这个方法需要实例化的时候单独调用
//所以我们可以放到Reactive装饰器上,就不需要再手动调用一次了
function Reactive() {
  return function <T extends { new (...args: any[]): {} }>(constructor: T){
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args)
        const target = reactive(this)
        if (target.watchFields) {
            target.watchFields()
        }
        return target
      }
    }
  }
}

如果将error的类型简单的写成Record<string,any>,

要是写成代码里的那样子,

具体的代码可以到(github.com/AndSpark/vu…) 这里看下。

具体使用

好了,现在我们的代码可以变成这个样子。

// ./form.ts
import { Type } from 'class-transformer'
import { IsEmail, IsMobilePhone, IsOptional, Length,
	MaxLength, MinLength,  ValidateNested } from 'class-validator'
import 'reflect-metadata'
// 需要引入 reflect-metadata 来使用metadata

class Profile {
    @IsOptional()
    avatar?: string

    @Length(2, 4, {message: '姓名长度应在2到4间'})
    realName: string

    @IsOptional()
    description?: string
}

// 在表单上可以设置初始值,现在是固定的。
// 那也可以通过属性装饰器调用api来设置动态初始值。大家可以自己实现试试看
@Reactive()
export class CreateUserForm extends Validator {
    @Length(4, 12, { message: '用户名长度应在4到12间' })
    username: string = ''

    @IsEmail({}, { message: '请填写正确的邮箱' })
    email: string = ''

    @IsMobilePhone('zh-CN', null, { message: '请输入正确的手机号码' })
    phone: string = ''

    @MinLength(4, { message: '密码长度不应低于4' })
    @MaxLength(12, { message: '密码长度不应大于12' })
    password: string = ''

    // 也可以关联其他表单类,但需要下面两个装饰器,用来关联
    @Type(() => Profile)
    @ValidateNested()
    profile: Profile = new Profile()
}
export default defineComponent({
setup() {
    const form = new CreateUserForm()
    return {
        form
    }
},
render() {
    const { form } = this
    return (
        <div>
            <field label='用户名' v-model={form.username} error={form.getError().username}></field>
            <field label='姓名' v-model={form.profile.realName} error={form.getError().profile?.realName}></field>
            <field label='邮箱' v-model={form.email} error={form.getError().email}></field>
            <field label='手机' v-model={form.phone} error={form.getError().phone}></field>
            <field label='密码' v-model={form.password} error={form.getError().password}></field>
            <button onClick={() => form.validate()}>验证</button>
            <button onClick={() => form.clearError()}>清空错误</button>
        </div>
    )
}
})

下面是简单的页面演示。

小结

其实装饰器能做的东西很多,也比较好玩。class-validator这个库里的装饰器还有很多,大家可以去github上看看。然而它提供的装饰器可能并不能完全满足我们的需求,所以还是需要自己去研究装饰器,去封装它。像这种表单,如果要设置初始值,我们也可以使用装饰器动态的调用接口来设置。

而且我们也许不需要通过继承的方式来实现验证器,而是通过将表单类传入到一个函数中,返回验证器。或者把验证器注入到表单中,有很多种方式来实现。

表单里的装饰器大多只用到了属性装饰器,其实方法装饰器也很有意思,类似拦截器,可以在方法调用前后执行你想要的操作,例如设置loading状态,完成错误处理,防抖节流等等。

如果大家想要尝试的话可以在vue中npm install vue-class-validator class-validator class-transformer reflect-metadata,或者去(github.com/AndSpark/vu…) 上看看(点个star)。

到此这篇关于VUE中使用TypeScript装饰器实现表单验证的文章就介绍到这了,更多相关VUE实现表单验证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue快速实现通用表单验证功能

    本文开篇第一句话,想引用鲁迅先生<祝福>里的一句话,那便是:"我真傻,真的,我单单知道后端整天都是CRUD,我没想到前端整天都是Form表单".这句话要从哪里说起呢?大概要从最近半个月的"全栈工程师"说起.项目上需要做一个城市配载的功能,顾名思义,就是通过框选和拖拽的方式在地图上完成配载.博主选择了前后端分离的方式,在这个过程中发现:首先,只要有依赖jQuery的组件,譬如Kendoui,即使使用了Vue,依然需要通过jQuery去操作DOM.其次,只有

  • vue中使用element-ui进行表单验证的实例代码

    element-ui 中验证 一.简单逻辑验证(直接使用rules) 实现思路 •html中给el-form增加 :rules="rules" •html中在el-form-item 中增加属性 prop="名称" •js中直接在data中定义rules:{} •html部分 <el-form ref="form" :rules="rules" :model="form" label-width=&q

  • Vue from-validate 表单验证的示例代码

    前言 需要进行表单数据验证,原先才用html5来完成验证,但是效果很差,也不够灵活,所以需要进行自定义的表单验证,网上的插件都太过庞大,项目并没有这么多的需求. 那让我们自己来写一个吧! 知识准备 vue的自定义指令 具体可以看官方手册,连接如下:https://vuejs.org/v2/guide/custom-directive.html 总的来说就是可以帮你在指定的钩子函数中跳用你的函数,参数(el,binding, vnode) el: 绑定的dom binding: 指令的各项属性 v

  • vue表单验证之禁止input输入框输入空格

    测试小姐姐让输入框不允许输入空格,安排. 刚开始用的下面这个方法,因为我是电脑端f12的情况下调试移动端,所以下面这个方法可以实现禁止输入空格,于是就打包测试上线了,上线后才发现真机中不支持,应该是pc端和移动端事件不一样,所以如果你是pc端,可以使用下面这个方法. input上添加下方代码(我用的vant也一样,包括elemenui等) @keydown.native="keydown($event)" methods中写入下方代码 methods:{ // 禁止输入空格 keydo

  • Vue快速实现通用表单验证的方法

    本文开篇第一句话,想引用鲁迅先生<祝福>里的一句话,那便是:"我真傻,真的,我单单知道后端整天都是CRUD,我没想到前端整天都是Form表单".这句话要从哪里说起呢?大概要从最近半个月的"全栈工程师"说起.项目上需要做一个城市配载的功能,顾名思义,就是通过框选和拖拽的方式在地图上完成配载.博主选择了前后端分离的方式,在这个过程中发现:首先,只要有依赖jQuery的组件,譬如Kendoui,即使使用了Vue,依然需要通过jQuery去操作DOM.其次,只有

  • vue elementui form表单验证的实现

    最近我们公司将前端框架由easyui 改为 vue+elementui .自学vue两周 就开始了爬坑之路.业余时间给大家分享一下心得,技术新手加上第一次分享(小激动),有什么不足的地方欢迎大家指正,多多交流才能共同进步! 1.问题 我们公司的项目比较大 表格 表单的页面都不胜数 ,基于此封装了一些 可复用的代码. 2.分析  vue给了我们不一样的前端代码体验  element ui 给我们一套功能强大的组件 减少了我们大量的开发时间 .双剑合璧 天下无敌!  但每个公司的代码风格不同  用户

  • Vue表单验证插件的制作过程

    前言 前段时间,老大搭好了Vue的开发环境,于是我们愉快地从JQ来到了Vue.这中间做的时候,在表单验证上做的不开心,看到vue的插件章节,感觉自己也能写一个,因此就自己开始写了一个表单验证插件va.js. 当然为什么不找个插件呢? vue-validator呀. 1.我想了下,一个是表单验证是个高度定制化的东西,这种网上找到的插件为了兼顾各个公司的需求,所以加了很多功能,这些我们不需要.事实证明,vue-validator有50kb,而我写的va.js只有8kb. 2.另一个是,vue-val

  • VUE中使用TypeScript装饰器实现表单验证的全过程

    目录 前言 装饰器 class-validator 封装Validator 具体使用 小结 前言 最近接触了关于很多TypeScript装饰器的知识,以及class-validator这个用装饰器来做表单验证的包,就萌生了想在vue中使用装饰器来做表单验证的想法.class-validator允许我们在类上通过使用装饰器来完成表单的验证,并且可在浏览器端和node端同时使用.那么接下来先简单介绍一下装饰器和class-validator的用法. 装饰器 装饰器的语法十分简单,只需要在想使用的装饰

  • vue动态绑定组件子父组件多表单验证功能的实现代码

    前端项目中经常会下拉或者选项卡,如果通过if,else或者switch去判断加载的话会产生大量冗余代码和变量定义,而且都写在一起后人很难维护. Vue核心在于组件,如果有内容通过选项卡或者下拉框切换用动态加载子组件最好不过. 如图: selects文件夹中,index只负责公共数据(当然公共数据也可以写在其他文件,只留一个入口文件),而comp文件夹中的几个组件则通过动态加载. 动态加载子组件:component // 给下拉框绑定下拉列表的索引 <el-select v-model="v

  • 微信小程序开发之表单验证WxValidate使用

    微信小程序的开发框架个人感觉大体上跟VUE是差不多的,但是他的表单组件没有自带的验证功能,因此开发小程序的表单验证时候一般有两种方法,一是自己裸写验证规则,但是需要比较扎实的正则表达式基础,一种是利用官方社区开发的WxValidate插件进行表单验证. WxValidate插件是参考 jQuery Validate 封装的,为小程序表单提供了一套常用的验证规则,包括手机号码.电子邮件验证等等,同时提供了添加自定义校验方法,让表单验证变得更简单. 首先插件的下载地址和官方文档都在WxValidat

  • vue中typescript装饰器的使用方法超实用教程

    VueConf ,尤大说, Vue 支持 Ts 了,网上关于 Vue + Ts 的资料有点少, 楼主踩了一个星期坑,终于摸明白了 修饰器 的玩法,下面我们就来玩下 Vue 的 decorator 吧 1,data 值的声明 在这里 public 声明的是公有属性, private 声明的是私有属性,私有属性要带 下划线 蓝色框里的内容是声明组件,在每个组件创建时都要带上, Components 中的写法如下 上面是 普通写法 ,下面是 懒加载写法 2.@Prop 父组件传值给子组件 父组件使用

  • Vue项目之ES6装饰器在项目实战中的应用

    目录 前言 装饰模式(Decorator) ES6 装饰器 装饰器应用 Validate CatchError Confirmation 总结 参考 前言 在面向对象(OOP)的设计模式中,装饰器的应用非常多,比如在 Java 和 Python 中,都有非常多的应用.ES6 也新增了装饰器的功能,本文会介绍 ES6 的装饰器的概念.作用以及在 Vue + ElementUI 的项目实战中的应用. 装饰模式(Decorator) 装饰模式(Decorator Pattern)允许向一个现有的对象添

  • 详解在Vue中使用TypeScript的一些思考(实践)

    Vue.extend or vue-class-component 使用 TypeScript 写 Vue 组件时,有两种推荐形式: Vue.extend():使用基础 Vue 构造器,创建一个"子类".此种写法与 Vue 单文件组件标准形式最为接近,唯一不同仅是组件选项需要被包裹在 Vue.extend() 中. vue-class-component:通常与 vue-property-decorator 一起使用,提供一系列装饰器,能让我们书写类风格的 Vue 组件. 两种形式输出

  • 在Vue 中使用Typescript的示例代码

    Vue 中使用 typescript 什么是typescript typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法.它很像JavaScript都强类型版本,除此之外,它还有一些扩展的语法,如interface/module等. typescript 在编译期会去掉类型和特有语法,生成纯粹的JavaScript. Typescript 5年内的热度随时间变化的趋势,整体呈现一个上升的趋势.也说明ts越来越️受大家的关注了. 安装typescrip

  • Vue 中使用 typescript的方法详解

    什么是typescript typescript 为 javaScript的超集,这意味着它支持所有都JavaScript都语法.它很像JavaScript都强类型版本,除此之外,它还有一些扩展的语法,如interface/module等. typescript 在编译期会去掉类型和特有语法,生成纯粹的JavaScript. Typescript 5年内的热度随时间变化的趋势,整体呈现一个上升的趋势.也说明ts越来越️受大家的关注了. 安装typescript npm install -g ty

  • vue中使用TypeScript的方法

    引言 近几年前端对 TypeScript的呼声越来越高,Typescript也成为了前端必备的技能.TypeScript 是 JS类型的超集,并支持了泛型.类型.命名空间.枚举等特性,弥补了 JS 在大型应用开发中的不足. 在单独学习 TypeScript时,你会感觉很多概念还是比较好理解的,但是和一些框架结合使用的话坑还是比较多的,例如使用 React.Vue 这些框架的时候与 TypeScript 的结合会成为一大障碍,需要去查看框架提供的.d.ts的声明文件中一些复杂类型的定义.组件的书写

  • vue中使用typescript配置步骤

    目录 1.vue老项目引入TypeScripe 从零开始创建vue+typescript项目 通过前端各个框架的发展,例如vue3.0,react和angular等框架的源码都是用ts(typescript)进行编写的,因此我感觉未来的中大型项目的发展趋势也离不开ts.因此我根据一些入门教程利用vue结合ts编写了文档,适合入门配置vue+ts项目. 1.vue老项目引入TypeScripe npm install vue-class-component vue-property-decorat

随机推荐