如何手写El-Form表单组件

开发 前端
最近又个项目让我有开始接触element-ui,想到当初对el-form表单有一些困惑,查看一下源码和一些技术文章,对el-form有一些新的认识。

[[415219]]

本文转载自微信公众号「前端有道」,作者星野。转载本文请联系前端有道公众号。

前言

在刚入行时候,只会知道如何使用表单组件,在后面一两年工作中也没有什么技术积累成为一个工具人,操作最多的就是ctrl+c和ctrl+v,在去年进了一家新公司,这家公司以前旧项目代码经过太多人的手,代码已经快不成人样了,难以维护,技术人员已经跑的差不多了,我进去好在让我们负责新的项目开发,要不然可能第二天就看不到我了,哈哈。项目主要面向于小程序和H5端,网上的UI库很难满足产品后续规划需求开发,只好开始研究组件原理及封装组件。

最近又个项目让我有开始接触element-ui,想到当初对el-form表单有一些困惑,查看一下源码和一些技术文章,对el-form有一些新的认识。

Form 表单

下面是一份el-form示例代码

  1. <template> 
  2.   <el-form :model="ruleForm" :rules="rules" ref="ruleForm"
  3.     <el-form-item label="名字" prop="pass"
  4.       <el-input type="password" v-model="ruleForm.pass"></el-input> 
  5.     </el-form-item> 
  6.     <el-form-item label="年龄" prop="age"
  7.       <el-input v-model.number="ruleForm.age"></el-input> 
  8.     </el-form-item> 
  9.     <el-form-item> 
  10.       <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button> 
  11.       <el-button @click="resetForm('ruleForm')">重置</el-button> 
  12.     </el-form-item> 
  13.   </el-form> 
  14. </template> 
  15.  
  16. <script> 
  17. export default { 
  18.   data() { 
  19.     return { 
  20.       ruleForm: { 
  21.         pass: ''
  22.         checkPass: ''
  23.         age: ''
  24.       }, 
  25.       rules: { 
  26.         pass: [{ required: true, message: '请输入名字'trigger'blur' }], 
  27.         age: [{ required: true, message: '请输入年龄'trigger'blur' }], 
  28.       }, 
  29.     } 
  30.   }, 
  31.   methods: { 
  32.     submitForm(formName) { 
  33.       this.$refs[formName].validate(valid => { 
  34.         if (valid) { 
  35.           alert('submit!'
  36.         } else { 
  37.           console.log('error submit!!'
  38.           return false 
  39.         } 
  40.       }) 
  41.     }, 
  42.     resetForm(formName) { 
  43.       this.$refs[formName].resetFields() 
  44.     }, 
  45.   }, 
  46. </script> 

首先要清楚一下组件的使用方式

1.el-form接收model和rule两个prop

  • model表示表单绑定的数据对象
  • rule表示验证规则策略,表单验证

2.el-form-item接收的prop属性,对应form组件的model数据中某个key值,如果rule刚好有key,给定的条件下去如失去焦点验证规则匹不匹配。

最终得到类似这样代码结构

  1. <template> 
  2.   <div> 
  3.     <form @submit.prevent> 
  4.       <div> 
  5.         姓名<input v-model="form.name" /> 
  6.       </div> 
  7.       <div> 
  8.         年龄<input v-model="form.age" /> 
  9.       </div> 
  10.       <div> 
  11.         <button @click="submit">提交</button> 
  12.       </div> 
  13.     </form> 
  14.   </div> 
  15. </template> 

手写表单组件

组件中嵌套组件,主要是通过slot插槽,可以将组件拼接成上面代码结构。代码如下

el-form

  1. <template> 
  2.     <form> 
  3.         <slot></slot> 
  4.     </form> 
  5. </template> 
  6. <script> 
  7. export default { 
  8.     name:'elForm' 
  9. </script> 

 

el-form-item

  1. <template> 
  2.     <div> 
  3.         <slot></slot> 
  4.     </div> 
  5. </template> 
  6. <script> 
  7. export default { 
  8.     name:'elFormItem' 
  9. </script> 

el-input

  1. <template> 
  2.     <input type="text"
  3. </template> 
  4. <script> 
  5. export default { 
  6.     name:'elInput' 
  7. </script> 

 接下来就要考虑到组件中的通讯。由于组件中有可能嵌套很多的组件,如果单纯通过$parent和$children查找出来的父级组件,不一定是el-form组件。

两个问题:

  • el-form-item组件如何得到el-form的数据
  • el-form组件如何和el-form-item进行交互

解决第一问题,可以通过provide与inject实现。解决第二问题,就要讲到dispatch派发和broadcast广播

provide与inject

通过provide将当前表单实例传递到所有后代组件中,后代通过inject接受传递的值。

el-form

  1. <template> 
  2.     <form><slot></slot></form> 
  3. </template> 
  4. <script> 
  5. export default { 
  6.     name:'elForm'
  7.     provide(){ 
  8.         return { 
  9.             elForm: this 
  10.         }   
  11.     }, 
  12.     props:{ 
  13.         model:{ 
  14.             type:Object, 
  15.             default:()=>({}) 
  16.         }, 
  17.         rules:Object 
  18.     } 
  19. </script> 

el-form-item

  1. <template> 
  2.     <div><slot></slot></div> 
  3. </template> 
  4. <script> 
  5. export default { 
  6.     name:'elFormItem'
  7.     inject:['elForm'], 
  8.     props:{ 
  9.         label:{  
  10.             type:String, 
  11.             default:'' 
  12.         }, 
  13.         prop:String  
  14.     }, 
  15.     mounted(){ 
  16.        console.log(this.elForm) 
  17.     } 
  18. </script> 

provide中this指el-form组件,this.elForm就能得到el-form组件中的数据和方法。

dispatch和broadcast广播

$dispatch与$broadcast是一种有历史的组件通信方式,因为他们是Vue1.0提供的一种方式,在Vue2.0中废弃了。

$dispatch: $dispatch会向上触发一个事件,同时传递要触发的祖先组件的名称与参数,当事件向上传递到对应的组件上时会触发组件上的事件侦听器,同时传播会停止。

$broadcast: $broadcast会向所有的后代组件传播一个事件,同时传递要触发的后代组件的名称与参数,当事件传递到对应的后代组件时,会触发组件上的事件侦听器,同时传播会停止(因为向下传递是树形的,所以只会停止其中一个叶子分支的传递)

$dispatch

  1. /** 
  2.  * 派发 (向上查找) (一个) 
  3.  * @param componentName // 需要找的组件的名称 
  4.  * @param eventName // 事件名称 
  5.  * @param params // 需要传递的参数 
  6.  */ 
  7.     dispatch(componentName, eventName, params) { 
  8.         let parent = this.$parent || this.$root;//$parent 找到最近的父节点 $root 根节点 
  9.         let name = parent.$options.name; // 获取当前组件实例的name 
  10.         // 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点 
  11.         // 循环出当前名称的一样的组件实例 
  12.         while (parent && (!name||name!==componentName)) { 
  13.             parent = parent.$parent; 
  14.             if (parent) { 
  15.                 name = parent.$options.name
  16.             } 
  17.         } 
  18.         // 有节点表示当前找到了name一样的实例 
  19.         if (parent) { 
  20.             parent.$emit.apply(parent,[eventName].concat(params)) 
  21.         } 
  22.     }, 

$broadcast

  1. /** 
  2.  * 派发 (向上查找) (一个) 
  3.  * @param componentName // 需要找的组件的名称 
  4.  * @param eventName // 事件名称 
  5.  * @param params // 需要传递的参数 
  6.  */ 
  7. broadcast(componentName, eventName, params) { 
  8. // 循环子节点找到名称一样的子节点 否则 递归 当前子节点 
  9. this.$children.map(child=>{ 
  10.     if (componentName===child.$options.name) { 
  11.         child.$emit.apply(child,[eventName].concat(params)) 
  12.     }else { 
  13.         broadcast.apply(child,[componentName,eventName].concat(params)) 
  14.     } 
  15. }) 

验证表单

async-validator是一个表单的异步验证的第三方库,也是element-ui 中的form组件所使用的验证方式。 

el-form-item

  1. <template> 
  2.   <div> 
  3.     <label v-if="label">{{label}}</label> 
  4.     <slot></slot> 
  5.     {{errorMessage}} 
  6.   </div> 
  7. </template> 
  8. <script> 
  9. import Schema from "async-validator"
  10. export default { 
  11.   name"elFormItem"
  12.   inject: ["elForm"], 
  13.   props: { 
  14.     label: { 
  15.       type: String, 
  16.       default"" 
  17.     }, 
  18.     prop: String 
  19.   }, 
  20.   data(){ 
  21.       return {errorMessage:''
  22.   }, 
  23.   mounted() { 
  24.     this.$on("validate", () => { 
  25.       if (this.prop) { 
  26.         let rule = this.elForm.rules[this.prop]; 
  27.         let newValue = this.elForm.model[this.prop]; 
  28.  
  29.         let descriptor = { 
  30.           [this.prop]: rule 
  31.         }; 
  32.         let schema = new Schema(descriptor); 
  33.          
  34.         return schema.validate({[this.prop]:newValue},(err,res)=>{ 
  35.             if(err){ 
  36.                 this.errorMessage = err[0].message; 
  37.             }else
  38.                 this.errorMessage = '' 
  39.             } 
  40.         }) 
  41.       } 
  42.     }); 
  43.   } 
  44. }; 
  45. </script> 

 

责任编辑:武晓燕 来源: 前端有道
相关推荐

2011-07-18 09:48:10

jQuery

2023-03-15 08:42:06

form表单设计接口

2016-12-13 13:54:10

EasyUI form数据加载

2009-07-23 16:59:31

ASP.NET认证Form表单

2009-07-03 13:24:56

JSP表单

2021-01-12 09:04:12

Django FormForm组件开发

2009-07-29 16:40:50

Ajax提交asp.n

2009-06-30 15:19:55

Form表单JSP入门

2022-01-25 18:11:55

vdomclassfunction

2024-10-05 00:00:05

高性能分布式IM

2021-02-20 12:34:53

鸿蒙HarmonyOS应用开发

2021-01-14 09:04:27

Django FormForm组件开发

2022-03-09 09:43:01

工具类线程项目

2016-09-27 19:28:37

2023-11-27 08:24:57

FormikReact

2022-09-22 12:38:46

antd form组件代码

2020-11-02 08:19:18

RPC框架Java

2021-03-18 08:04:54

AQS工具CAS

2022-09-27 08:01:48

递归函数GScript

2022-10-17 09:08:01

点赞
收藏

51CTO技术栈公众号