原文:仿 ElmentUI 实现一个 Form 表单

一、目标

  ElementUI 中 Form 组件主要有以下 功能 / 模块:

  • Form
  • FormItem
  • Input
  • 表单验证

  在这套组件中,有 3 层嵌套,这里面使用的是 slot 插槽,我们在接下来的代码中也需要运用到它。

二、组件设计

  • e-form 全局校验,并提供插槽;
  • e-form 单一项校验和显示错误信息,并提供插槽;
  • e-input 负责数据的双向绑定

三、开始

e-input

<template>
<div>
<!-- 1.绑定 value 2.响应 input 事件 -->
<input type="text" :value="valueInInput" @input="handleInput">
</div>
</template> <script>
export default {
name: 'EInput',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
valueInInput: this.value // 确保数据流的单向传递
}
},
methods: {
handleInput(e) {
this.valueInInput = e.target.value;
this.$emit('input', this.valueInInput); // 此处提交的事件名必须是 ‘input’ // 数据变了,定向通知 formItem 进行校验
this.dispatch('EFormItem', 'validate', this.valueInInput);
}, dispatch(componentName, eventName, params) { // 查找指定 name 的组件,
let parent = this.$parent || this.$root;
let name = parent.$options.name while(parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
if (parent) {
// 这里,我们不能用 this.$emit 直接派发事件,因为在 FormItem 组件中,Input 组件的位置只是一个插槽,无法做事件监听,
// 所以此时我们让 FormItem 自己派发事件,并自己监听。修改 FormItem 组件,在 created 中监听该事件。
parent.$emit.apply(parent, [eventName].concat(params));
}
}
}
}
</script>

这里需要注意的是 v-model 绑定的值与 props 传递的值的关系,从而能将修改后的值暴露至顶层自定义组件。使用如下:

<template>
<div id="app">
<e-input v-model="initValue"></e-input>
<div>{{ initValue }}</div>
</div>
</template> <script>
import EInput from './components/Input.vue'; export default {
name: "app",
components: {
EInput
},
data() {
return {
initValue: '223',
};
},
};
</script>

FormItem 的设计

<template>
<div>
<label v-if="label">{{ label }}</label>
<div>
<!-- 拓展槽 -->
<slot></slot>
<!-- 验证提示信息 -->
<p v-if="validateState === 'error'" class="error">{{ validateMessage }}</p>
</div>
</div>
</template> <script>
import AsyncValidator from 'async-validator'; export default {
name: 'EFormItem',
props: {
label: { type: String, default: '' }, // 表单项的名称
prop: { type: String, default: '' } // 表单项的自定义 prop
},
inject: ['eForm'], // provide/inject,vue 跨层级通信
data() {
return {
validateState: '',
validateMessage: ''
}
},
methods: {
validate() {
return new Promise(resolve => {
const descriptor = {}; // async-validator 建议用法;
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) { // 查找上级指定 name 的组件
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));
}
}
},
created() {
this.$on('validate', this.validate); // 'validate' 事件由 e-input 组件通知,在 e-form-item 组件接收到后自行触发对应方法
},
// 因为我们需要在 Form 组件中校验所有的 FormItem ,
// 所以当 FormItem 挂载完成后,需要派发一个事件告诉 Form :你可以校验我了。
mounted() {
// 当 FormItem 中有 prop 属性的时候才校验,
// 没有的时候不校验。比如提交按钮就不需要校验。
if (this.prop) {
this.dispatch('EForm', 'addFiled', this);
}
}
}
</script> <style scoped>
.error {
color: red;
}
</style>

其中, methods 中的方法均是辅助方法,validate() 是异步校验的方法。

Form 的设计

<template>
<form>
<slot></slot>
</form>
</template> <script>
export default {
name: 'EForm',
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
},
provide() { // provide/inject,vue 跨层级通信
return {
eForm: this // form 组件实例, 在其他组件中可以通过 this.xxx 来获取属性/方法
}
},
data() {
return {
fileds: [] // 需要校验的 e-form-item 组件数组
}
},
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)
}
},
created() {
// 缓存需要检验的组件
this.fileds = [];
this.$on('addFiled', filed => this.fileds.push(filed))
}
}
</script>

仿 ELEMENTUI 实现一个简单的 Form 表单的更多相关文章

  1. 写一个简单的form表单,当光标离开表单的时候表单的值发送给后台

    <body> <form action="index.php"> <input type="text" name="tx ...

  2. asp.net使用post方式action到另一个页面,在另一个页面接受form表单的值!(报错,已解决!)

    原文:asp.net使用post方式action到另一个页面,在另一个页面接受form表单的值!(报错,已解决!) 我想用post的方式把一个页面表单的值,传到另一个页面.当我点击Default.as ...

  3. 实现一个兼容eleUI form表单的多选组件

    本质上是实现了一个eleUI select组件中的创建条目功能的组件,仅仅是将dropdown的选择框变成了label形式.支持eleUI的form表单校验,同时组件也提供了组件内自定义校验的方法.常 ...

  4. 简单的form表单操作(Servlet)

    Servlet模型 [表单的处理][模型(1)] ●HTML网页 <!DOCTYPE html> <html> <head> <meta charset=&q ...

  5. js简单校验form表单

    /** * 数据简单校验 */ function checkData (formId) { var check = true; var emailReg = new RegExp("^[a- ...

  6. 利用js编写一个简单的html表单验证,验证通过时提交数据(附源码)

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  7. jQuery 实现最简单的form表单提交 Loding 功能

    <html> <head><title></title></head> <body> <form name="e ...

  8. 简单的form表单

    效果 html <ul class="edit_list"> <li><em>*</em><span class=" ...

  9. FastAPI 学习之路(十六)Form表单

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

随机推荐

  1. ASP.NET Core - 开篇

    由来 ASP.NET Core 是一个跨平台的高性能开源框架,ASP.NET Core第一次出现在我们眼前是以 ASP.NET vNext 命名的,然后又重新命名为ASP.NET 5,为了表明它并不是 ...

  2. 简简单单的Vue4(vue-cie@3.x,vue’Debug[调试],vue‘sHttp)

    既然选择了远方,便只顾风雨兼程! __HANS许 系列:零基础搭建前后端分离项目 系列:零基础搭建前后端分离项目 vue-cli@3.x 创建项目 Vue的Debug(调试) Vue的Http请求 提 ...

  3. QT5.6.0 VS2013 Win764位系统QT环境搭建过程

    QT5.6.0 VS2013 Win764位系统QT环境搭建过程 没用过QT自己跟同事要了安装包,按照同事指导方法操作安装部署开发环境结果遇到好多问题,错误网上搜遍了所有帖子也没有找到合适的解决方案. ...

  4. OO_BLOG2_多线程电梯模拟

    作业2-1 单部多线程傻瓜调度(FAFS)电梯的模拟 I. 基于度量的程序结构分析 1)程序结构与基本度量统计图 2)分析 ​ 这次作业基本奠定了本人三次电梯作业的基本架构,简述如下: Elevato ...

  5. div悬浮窗口设计来完成注册页面

    login.jsp页面 <script type="text/javascript" src="js/register.js"></scrip ...

  6. Deepin MongoDB安装&使用总结

    参考:手把手教你 MongoDB 的安装与详细使用(一) deepin 安装 mongodb 数据库(全面) 1. 导入公钥 sudo apt-key adv --keyserver hkp://ke ...

  7. 在虚拟机中搭建qduoj(一)——准备工作

    为缩减篇幅,已略去ubuntu镜像下载.虚拟机(VirtualBox.VMware)安装等操作,若有疑问请搜索相关教程. 1.虚拟机系统安装 本教程使用Ubuntu16.04 server版本(des ...

  8. arcgis api for js入门开发系列二十打印地图的那些事

    前面我写过关于利用arcgis api for js打印地图的,但是打印地图服务都是基于arcgis server发布的,arcgis api加载在线地图,比如天地图.百度地图.高德地图等,底图都是打 ...

  9. (详细)华为Mate7 MT7-TL00的usb调试模式在哪里开启的步骤

    就在我们使用pc连接安卓手机的时候,如果手机没有开启usb调试模式,pc则不能够成功识别我们的手机,在一些情况下,我们使用的一些功能较好的工具好比之前我们使用的一个工具引号精灵,老版本就需要打开usb ...

  10. 小米平板6.0系统如何无ROOT激活xposed框架的步骤

    在较多企业的引流,或业务操作中,基本上都需要使用安卓的黑高科技术Xposed框架,近期,我们企业购买了一批新的小米平板6.0系统,基本上都都是基于7.0以上系统,基本上都不能够获得ROOT的su权限, ...