用vue开发一套响应式通用表单组件

255 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

前些天研究了一下Elementui Form表单的源码,闲得无聊便开始琢磨想自己开发一套通用的表单组件来总结和复习一下,顺便检验一下自己的成果

组件

input组件

From表单需要有最基础的填写表单功能,所以需要创建一个input组件,在input内需要接收传入的参数value,还需要把input内输入的数据回传到调用它的组件

<template>
  <input :value="value" @input="onInput" v-bind="$attrs">
</template>
// js
props: {
    value: {
      type: String,
      default: ''
    },
},
methods:{
    onInput(e) {
      this.$emit('input', e.target.value)
    }
}

调用input组件

创建一个form表单组件,在组件内引入咱们刚刚创建好的input组件,给它设置上v-model进行实时响应

<template>
    <div>
      <L-input v-model="model.username"></L-input>
    </div>
</template>

formItem组件实现

众所周知<el-form-item>是表单内包含一个或者多个输入模块,需要自带校验功能,如果错误需要有错误提示。

  <div>
    <label v-if="label">{{label}}</label>
    <slot></slot>
    <p v-if="error">{{error}}</p>
  </div>

label是输入模块的名称,咱们从组件内接收传入参数。error为错误提示。

调用formitem组件

跟elementuiForm表单使用方法类似,在<L-form-item>内包裹<L-input>标签,传入label名称和prop当前字段的名称,这样好区分。v-model为当前输入的值。

  <template>
    <div>
      <h3>表单</h3>
    <hr>
    <L-form-item label="⽤户名" prop="username">
      <L-input v-model="model.username"></L-input>
    </L-form-item>
    </div>
  </template>

form组件实现

  • 引入form标签,在内写入插槽
<template>
    <form>
        <slot></slot>
    </form>
</template>
  • 将组件实现作为提供者,子组件可以方便获取,给from绑定当前this,provide为高阶组件插件
provide() {
     return {
         form: this
     };
 },
  • 必填项和内容校验,在props内分别设置model和rules
props: {
 model: { type: Object, required: true },
 rules: { type: Object }
}

调用form组件

  • 嵌套 和饿了么组件使用一样放入咱们写好的formItem组件和在内嵌套input组件
<k-form :model="model" :rules="rules" ref="loginForm">
  <L-form-item label="⽤户名" prop="username">
      <L-input v-model="model.username"></L-input>
  <L-form-item>
</k-form>
  • 校验规则 校验规则rules内设置分别设置必填项和提示语句,这个做法和传统方式一样(咱们做的是一个简单实用组件)
rules: {
 username: [{ required: true, message: "请输⼊⽤户名" }],
}

流程梳理

  • input通知校验 解释:大概的意思是把input值传入到formItem内进行校验,不能单独从input内进行校验, 在input组件内把validate传入到父组件
$parent指FormItem
onInput(e) {
 this.$parent.$emit('validate');
}
  • formitem监听校验通知,获取规则并执行校验
  1. 注入form
inject:['form']
  1. 在渲染完dom后监听校验事件
mounted(){
 this.$on('validate', () => { this.validate() })
},
  1. 表单全局校验validate方法实现 这里就需要npm的插件 async-validator,安装:npm i async-validator -S
引入插件
import Schema from "async-validator"

获取对应formItem的校验规则

const rules = this.form.rules[this.prop];

获取校验值

const value = this.form.model[this.prop]

校验描述对象和创建校验器

const descriptor = { [this.prop]: rules }
const schema = new Schema(descriptor)

最后,用promise尽心返回数据,如果没有进入到catch则说明校验通过

  return schema.validate({ [this.prop]: value }, errors => {
    if (errors) {
      this.error = errors[0].message;
    } else {
      this.error = "";
    }
  });
  1. validate,进行校验 调用所有含有prop属性的子组件的validate方法合并到promise数组内
validate(cb) {
    const tasks = this.$children.filter(item => item.prop).map(item => item.validate())
    Promise.all(tasks).then(() => cb(true)).catch(() => cb(false))
}

Promise.all所有任务必须全部成功后才算校验通过,如果进入到catch则失败

最后

在开发中引用了async-validator插件,如果不嫌麻烦也可以自己写一套,经过多次的调试,终于终于把这个实现了。加油!