您的当前位置:首页如何用Vue自己写一个简易版elementUI的form组件

如何用Vue自己写一个简易版elementUI的form组件

2024-12-12 来源:哗拓教育
完整源代码请移步GitHub:

要仿照的elementUI的效果:

<el-form :model="ruleForm" :rules="rules" ref="loginForm">
  <el-form-item label="用户名" prop="name">
    <el-input v-model="ruleForm.name"></el-input>
  </el-form-item>
  <el-form-item label="密码" prop="pwd">
    <el-input v-model="ruleForm.pwd"></el-input>
  </el-form-item>
</el-form>

实现思路

Step1:实现一个支持 v-model 的自定义输入组件

要实现的结果如下:保证 l-input 可以使用 v-model 来进行数据的双向绑定

<l-input v-model="inputValue" :type="type"></l-input>

v-model:

  • 原理:
 <l-input :value="inputValue" @input="inputValue = $event"></l-input>
  • 把 inputValue 传给组件 l-input 内部
  • 当组件 l-input 的 input 事件被触发时自动将事件携带的参数赋值给 inputValue

$event:

当在父级组件监听事件的时候,我们可以通过 $event 访问到被抛出的值


代码

l-input 组件实现代码如下:

<template>
  <!-- LInput组件 -->
  <div class='l-input'>
    <input :type="type" :value="value" @input="handlerInput">
  </div>
</template>

这个组件内的 <input> 必须:

  • 将其 value 特性绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
  export default {
    props: {
      value: {
        type: String,
        default: ''
      },
      type: {
        type: String,
        default: ''
      }
    },
    methods: {
      handlerInput(e) {
        // 通知父组件值更新
        this.$emit('input', e.target.value)

        // 通知 formItem 进行校验
        this.$parent.$emit('validate',e.target.value)
      }
    },
  }

Step2:实现一个支持 负责label标签 以及 显示错误信息的组件

要实现的结果如下:保证 l-form-item 可以显示 label 以及可以对 prop 进行数据校验

<l-form-item label='用户名' prop='name'>
  <l-input v-model="ruleForm.name" type="text"></l-input>
</l-form-item>

代码

<template>
  <div>
    <label class='label' v-if="label">{{label}} </label>
    <slot></slot>
    <span class='tips' v-if="validateStatus === 'error'">{{validateMsg}}</span>
  </div>
</template>
  import schema from 'async-validator'
  export default {
    inject: ["form"], // 注入form  获取到 form 的 model 以及 rules
    props: {
      label: {
        type: String,
        default: ''
      },
      prop: {
        type: String,
        default: ''
      }
    },

    data() {
      return {
        validateStatus: '',
        validateMsg: ''
      }
    },
    
    created() {
      // 校测到来此 LInput 的检测请求
      this.$on('validate', this.validate)
    },

    mounted() {
      // 挂载到form上时,派发一个添加事件
      // 如果此组件传入了 props ,则需要通知父组件 以放入检测数组
      if (this.prop) {
        this.$parent.$emit("formItemAdd", this)
      }
    },

    methods: {
      validate() {
        // 使用 async-validator 进行校验
        return new Promise((resolve) => {
          console.log('validate -- input 通知我校验数据了', this.form.model[this.prop])
          // 校验规则
          let descriptor = {
            [this.prop]: this.form.rules[this.prop]
          }

          // 校验器
          let validator = new schema(descriptor)

          // 开始校验数据
          validator.validate({[this.prop]: this.form.model[this.prop]}, (error) => {
            if(error) {
              this.validateStatus = 'error'
              this.validateMsg = error[0].message

              resolve(false)
            } else {
              this.validateStatus = ''
              this.validateMsg = ''

              resolve(true)
            }
          })
        })
      }
    },
  }

Step3:实现一个支持 负责label标签 以及 显示错误信息的组件

要实现的结果如下:传入 表单数据对象(ruleForm) 和 表单验证规则(rules)后可以实现表单的数据校验 提供 validate 方法来获取整个表单的验证结果

<l-form :model='ruleForm' :rules='rules' ref='loginForm'>
  <l-form-item label='用户名' prop='name'>
    <l-input v-model="ruleForm.name" type="text"></l-input>
  </l-form-item>
  <l-form-item label='密码' prop='pwd'>
    <l-input v-model="ruleForm.pwd" type="password"></l-input>
  </l-form-item>
  <l-form-item>
    <button @click='submitForm'>signIn</button>
  </l-form-item>
</l-form>
// 传入的表单数据对象和表单验证规则
ruleForm: {
  name: "",
  pwd: ""
},
rules: {
  name: [
    { required: true, message: "请输入名称" },
    { min: 6, max: 10, message: "请输入6~10位用户名" }
  ],
  pwd: [{ required: true, message: "请输入密码" }]
}
// 调用组件的validate方法获取验证结果
this.$refs.loginForm.validate(valid => {
  if (valid) {
    alert("提交登录!")
  } else {
    console.log("校验失败")
    return false
  }
})

代码

<template>
  <div>
    <slot></slot>
  </div>
</template>
  export default {
    provide() {
      return {
        form: this // 将表单实例传递给后代
      }
    },
    props: {
      model: {
        type: Object,
        required: true
      },
      rules: {
        type: Object
      }
    },
    data() {
      return {
        files: []
      }
    },
    created() {
      // 缓存需要校验的表单项
      this.$on('formItemAdd', item => this.files.push(item))
      console.log(this.files)
    },

    methods: {
      // 将FormItem数组转换为 validate() 返回的promise数组
      // 调用 LForm 组件的 validate 方法可以得到表单数据校验的结果
      async validate(callback) {
        let tasks = this.files.map(item => item.validate())
        console.log(tasks)
        
        // 获取所有结果统一处理
        const results = await Promise.all(tasks)

        let ret = true
        results.forEach(valid => {
          if (!valid) {
            ret = false // 只要一个失败就失败
          }
        })
        callback(ret)
      }
    },
  }
显示全文