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

完整源代码请移步GitHub: https://github.com/webxing/element_form

要仿照的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)
      }
    },
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容