仿ElementUI实现一个Form表单的实现代码(IT技术)

您所在的位置:网站首页 elementui弹出表单 仿ElementUI实现一个Form表单的实现代码(IT技术)

仿ElementUI实现一个Form表单的实现代码(IT技术)

2022-03-24 08:21| 来源: 网络整理| 查看: 265

我们对上面的代码做一点解释:

**解释一:**既然我们想做一个 Input 组件,那么接收的值必然是父组件传进来的,并且当父组件没有传进来值的时候,我们可以它一个默认值 "" 。

**解释二:**我们在设计组件的时候,要遵循单向数据流的原则:父组件传进来的值,我们只能用,不能改。那么将父组件传进来的值进行一个赋值操作,赋值给 Input 组件内部的 valueInInput ,如果这个值发生变动,我们就修改内部的值 valueInInput 。这样我们既可以处理数据的变动,又不会直接修改父组件传进来的值。

**解释三:**当 Input 中的值发生变动时,触发 @input 事件,此时我们通过 event.target.value 获取到变化后的值,将它重新赋值给内部的 valueInInput 。

**解释四:**完成了内部赋值之后,我们需要做的就是将变化后的值通知父组件,这里我们用 this.$emit 向上派发事件。其中第一个参数为事件名,第二个参数为变化的值。

完成了以上四步,一个实现了双向数据绑定的简单的 Input 组件就设计完成了。此时我们可以在 App.vue 中引入 Input 组件观察一下结果。

{{ initValue }} import EInput from './components/Input.vue'; export default { name: "app", components: { EInput }, data() { return { initValue: '223', }; }, };

 

2. FormItem 的设计

在 ElementUI 的 formItem 中,我们可以看到:

需要 label 来显示名称; 需要 prop 来校验当前项; 需要给 input 或 button 预留插槽;

根据上面的需求,我们可以创建出自己的 formItem ,新建一个 FormItem.vue 文件 。

{{ label }}

{{ validateMessage }}

export default { name: "EFormItem", props: { label: { type: String, default: '' }, prop: { type: String, default: '' } }, data() { return { validateState: '', validateMessage: '' } }, } .error { color: red; }

和上面一样,我们接着对上面的代码进行一些解释:

**解释一:**根据 ElementUI 中的用法,我们知道 label 是父组件传来,且当传入时我们展示,不传入时不展示。

解释二: slot 是一个预留的槽位,我们可以在其中放入 input 或其他组件、元素。

解释三: p 标签是用来展示错误信息的,如果验证状态为 error 时,就显示。

此时,我们的 FormItem 组件也可以使用了。同样,我们在 App.vue 中引入该组件。

{{ ruleForm }} import EInput from './components/Input.vue'; import EFormItem from './components/FormItem.vue'; export default { name: "app", components: { EInput, EFormItem }, data() { return { ruleForm: { name: '', pwd: '', }, }; }, };

 

3. Form 的设计

到现在,我们已经完成了最内部的 input 以及中间层的 FormItem 的设计,现在我们开始设计最外层的 Form 组件。

当层级过多并且组件间需要进行数据传递时,Vue 为我们提供了 provide 和 inject API,方便我们跨层级传递数据。

我们举个例子来简单实现一下 provide 和 inject 。在 App.vue 中,我们提供数据(provide)。

export default { name: "app", provide() { return { msg: '哥是最外层提供的数据' } } };

接着,我们在最内层的 Input.vue 中注入数据,观察结果。

{{ msg }} export default { name: "EInput", inject: [ 'msg' ], props: { value: { type: String, default: '', } }, data() { return { valueInInput: this.value }; }, methods: { handleInput(event) { this.valueInInput = event.target.value; this.$emit('input', this.valueInInput); } }, };

根据上图,我们可以看到无论跨越多少层级, provide 和 inject 可以非常方便的实现数据的传递。

理解了上面的知识点后,我们可以开始设计 Form 组件了。

根据 ElementUI 中表单的用法,我们知道 Form 组件需要实现以下功能:

提供数据模型 model; 提供校验规则 rules; 提供槽位,里面放我们的 FormItem 等组件;

根据上面的需求,我们创建一个 Form.vue 组件:

export default { name: 'EForm', props: { // 解释一 model: { type: Object, required: true }, rules: { type: Object } }, provide() { // 解释二 return { eForm: this // 解释三 } } }

解释一:该组件需要用户传递进来一个数据模型 model 进来,类型为 Object 。 rules 为可传项。

解释二:为了让各个层级都能使用 Form 中的数据,需要依靠 provide 函数提供数据。

解释三:直接将组件的实例传递下去。

完成了 Form 组件的设计,我们在 App.vue 中使用一下:

提交 import EInput from './components/Input.vue'; import EFormItem from './components/FormItem.vue'; import EForm from "./components/Form"; export default { name: "app", components: { EInput, EFormItem, EForm }, data() { return { ruleForm: { name: '', pwd: '', }, rules: { name: [{ required: true }], pwd: [{ required: true }] }, }; }, };

到目前为止,我们的基本功能就已经实现了,除了提交与验证规则外,所有的组件几乎与 ElementUI 中的表单一模一样了。下面我们就开始实现校验功能。

4. 设计校验规则

在上面设计的组件中,我们知道校验当前项和展示错误信息的工作是在 FormItem 组件中,但是数据的变化是在 Input 组件中,所以 FormItem 和 Input 组件是有数据传递的。当 Input 中的数据变化时,要告诉 FormItem ,让 FormItem 进行校验,并展示错误。

首先,我们修改一下 Input 组件:

methods: { handlerInput(event) { this.valueInInput = event.target.value; this.$emit("input", this.valueInInput); // 数据变了,定向通知 FormItem 校验 this.dispatch('EFormItem', 'validate', this.valueInput); }, // 查找指定 name 的组件, dispatch(componentName, eventName, params) { var parent = this.$parent || this.$root; var name = parent.$options.name; while (parent && (!name || name !== componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.name; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } } }

这里,我们不能用 this.$emit 直接派发事件,因为在 FormItem 组件中, Input 组件的位置只是一个插槽,无法做事件监听,所以此时我们让 FormItem 自己派发事件,并自己监听。修改 FormItem 组件,在 created 中监听该事件。

created() { this.$on('validate', this.validate); }

当 Input 组件中的数据变化时, FormItem 组件监听到 validate 事件后,执行 validate 函数。

下面,我们就要处理我们的 validate 函数了。而在 ElementUI 中,验证用到了一个底层库async-validator,我们可以通过 npm 安装这个包。

npm i async-validator

async-validator 是一个可以对数据进行异步校验的库,具体的用法可以参考上面的链接。我们通过这个库来完成我们的 validate 函数。继续看 FormItem.vue 这个文件:

{{ label }}

{{ validateMessage }}

import AsyncValidator from "async-validator"; export default { name: "EFormItem", props: { label: { type: String, default: '' }, prop: { type: String, default: '' } }, inject: ["eForm"], // 解释一 created() { this.$on("validate", this.validate); }, mounted() { // 解释二 if (this.prop) { // 解释三 this.dispatch('EForm', 'addFiled', this); } }, data() { return { validateMessage: "", validateState: "" }; }, methods: { validate() { // 解释四 return new Promise(resolve => { // 解释五 const descriptor = { // name: this.form.rules.name => // name: [ { require: true }, { ... } ] }; descriptor[this.prop] = this.eForm.rules[this.prop]; // 校验器 const validator = new AsyncValidator(descriptor); const model = {}; model[this.prop] = this.eForm.model[this.prop]; // 异步校验 validator.validate(model, errors => { if (errors) { this.validateState = "error"; this.validateMessage = errors[0].message; resolve(false); } else { this.validateState = ""; this.validateMessage = ""; resolve(true); } }); }); }, // 查找上级指定名称的组件 dispatch(componentName, eventName, params) { var parent = this.$parent || this.$root; var name = parent.$options.name; while (parent && (!name || name !== componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.name; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } } } }; .error { color: red; }

我们对上面的代码做一个解释。

解释一:注入 Form 组件提供的数据 - Form 组件的实例,下面就可以使用 this.eForm.xxx 来使用 Form 中的数据了。

解释二:因为我们需要在 Form 组件中校验所有的 FormItem ,所以当 FormItem 挂载完成后,需要派发一个事件告诉 Form :你可以校验我了。

解释三:当 FormItem 中有 prop 属性的时候才校验,没有的时候不校验。比如提交按钮就不需要校验。

**解释四:**返回一个 promise 对象,批量处理所有异步校验的结果。

解释五: descriptor 对象是 async-validator 的用法,采用键值对的形式,用来检查当前项。比如:

// 检查当前项 // async-validator 给出的例子 name: { type: "string", required: true, validator: (rule, value) => value === 'muji', }

FormItem 中检查当前项完成了,现在我们需要处理一下 Form 组件中的全局校验。表单提交时,需要对 form 进行一个全局校验。大致的思路是:循环遍历表单中的所有派发上来的 FormItem ,让每一个 FormItem 执行自己的校验函数,如果有一个为 false ,则校验不通过;否则,校验通过。我们通过代码实现一下:

export default { props: { model: { type: Object, required: true }, rules: { type: Object } }, provide() { return { eForm: this, // provide this component's instance } }, data() { return { fileds: [], } }, created() { // 解释一 this.fileds = []; this.$on('addFiled', filed => this.fileds.push(filed)); }, methods: { async validate(cb) { // 解释二 // 解释三 const eachFiledResultArray = this.fileds.map(filed => filed.validate()); // 解释四 const results = await Promise.all(eachFiledResultArray); let ret = true; results.forEach(valid => { if (!valid) { ret = false; } }); cb(ret); } }, }

解释一:用 fileds 缓存需要校验的表单项,因为我们在 FormItem 中派发了事件。只有需要校验的 FormItem 会被派发到这里,而且都会保存在数组中。

if (this.prop) { this.dispatch('EForm', 'addFiled', this); }

解释二:当点击提交按钮时,会触发这个事件。

解释三:遍历所有被添加到 fileds 中的 FormItem 项,让每一项单独去验证,会返回 Promise 的 true 或 false 。将所有的结果,放在一个数组 eachFiledResultArray 中。

解释四:获取所有的结果,统一进行处理,其中有一个结果为 false ,验证就不能通过。

至此,一个最简化版本的仿 ElementUI 的表单就实现了。

 

四. 总结

当然上面的代码还有很多可以优化的地方,比如说 dispatch 函数,我们可以写一遍,使用的时候用 mixin 导入。由于篇幅关系,这里就不做处理了。

通过这次实现,我们首先总结一下其中所涉及的知识点。

父组件传递给子组件用 props 子组件派发事件,用 $emit 跨层级数据交互,用 provide 和 inject 用 slot 可以预留插槽

其次是一些思想:

单项数据流:父组件传递给子组件的值,子组件内部只能用,不能修改。 组件内部的 name 属性,可以通过 this.$parent.$options.name 查找。 想要批量处理很多异步的结果,可以用 promise 对象。

最后,文章会首先发布在我的 Github ,以及公众号上,欢迎关注,欢迎 star。

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

相关文章基于vue-cli、elementUI的Vue超简单入门小例子(推荐)js form表单input框限制20个字符,10个汉字代码实例vue项目创建并引入饿了么elementUI组件的步骤优雅的将ElementUI表格变身成树形表格的方法步骤基于Vue+elementUI实现动态表单的校验功能(根据条件动态切换校验格式vue elementUI table表格数据 滚动懒加载的实现方法elementUI多选框反选的实现代码使用mixins实现elementUI表单全局验证的解决方法Vue在 Nuxt.js 中重定向 404 页面的方法vue项目首屏加载时间优化实战 猜您喜欢

vue项目首屏加载时间优化实战

单页面应用的一个问题就是首页加载东西过多,加载时间过长。特别在挪动端,单页面应用的首屏加载优化更是绕不开的话题,这篇文章主要引见了vue项目首屏加载时间优化实战,感兴味的小同伴们能够参考一下..

灵活使用console让js调试更简单的方法步骤

这篇文章主要引见了灵敏运用console让js调试更简单的办法步骤,恰当运用这些办法能够使调试更容易,更快速,更直观,小编觉得挺不错的,如今分享给大家,也给大家做个参考。一同跟随小编过来看看吧.. 05-16灵活使用console让js调试更简单的方法步骤 05-16详解实现一个通用的“划词高亮”在线笔记功能 05-16Vue源码学习之关于对Array的数据侦听实现 05-16vue的keep-alive中使用EventBus的方法 05-16js继承的这6种方式!(上) 05-16jQuery对底部导航进行跳转并高亮显示的实例代码 网友评论 推荐文章 细数软件工程各阶段必不可少的那些图

细数软件工程各阶段必不可少的那些图

各种反弹shell方法总结

各种反弹shell方法总结

Mabitis

Unable to preventDefault inside passive event listener due to target being treated as passive

各种反弹shell方法总结

Java中的循环语句

MySQL 索引

git recommend(alive)

[白话解析] 深入浅出支持向量机(SVM)之核函数

html小工具——文章注释编辑器

最新文章 1node.js基于socket.io快速实现一个实时通讯应用 2深入浅出 Vue 系列 -- 数据劫持实现原理 3vue 中 beforeRouteEnter 死循环的问题 4JavaScript中十种一步拷贝数组的方法实例详解 5vue watch关于对象内的属性监听 6vue项目中仿element-ui弹框效果的实例代码 7对于防止按钮重复点击的尝试详解 8Vue render函数实战之实现tabs选项卡组件 9详解Vue依赖收集引发的问题 10JS大坑之19位数的Number型精度丢失问题详解

Copyright 2022 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3