引言

在工作中我经常需要处理一些复杂、动态表单,但是随着需求不断迭代,我们也许会发现曾经两三百行的.vue文件现在不知不觉到了两千行,三千行,甚至更多...

这对于一个需要长期维护的项目,无疑是增加了很多难度。

因此,为了减小文件大小,优化表单组织的结构,我在日常的开发中实践出一种基于组件的表单拆分方法,同时还能保证所有的表单项是处于同一个el-form中。

这对于一个一开始就没有做好文件组织,组件化的项目,有以下几个优点:

  1. 改动小!后续新增表单项基本不会改动以前的代码
  2. 基于组件!在逻辑上对表单项做出拆分,并在任何地方嵌入
  3. 易维护!化单个大组件为多个小组件,每个组件只专注于一部分表单。

表单拆分

接下来我们会通过完成一个实际表单的方式来介绍如何实践这种表单组织方式。

element-ui文档中的这个表单为例,接下来尝试用我们的方式来实现

首先假设我们当前有一个vue文件 ./form/myForm.vue

<template>
<el-form ref="form" :model="form" label-width="140px">
...
</el-form>
<template>
<script>
export default {
name: 'myForm',
data() {
return {
form: {}
}
}
}
</script>

如果我们直接按照element-ui的表单文档来写,那么我们的myForm.vue文件可能就会变成这样:

<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="活动名称">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="活动区域">
<el-select v-model="form.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="活动时间">
<el-col :span="11">
<el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
</el-col>
<el-col class="line" :span="2">-</el-col>
<el-col :span="11">
<el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
</el-col>
</el-form-item>
<el-form-item label="即时配送">
<el-switch v-model="form.delivery"></el-switch>
</el-form-item>
<el-form-item label="活动性质">
<el-checkbox-group v-model="form.type">
<el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>
<el-checkbox label="地推活动" name="type"></el-checkbox>
<el-checkbox label="线下主题活动" name="type"></el-checkbox>
<el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="特殊资源">
<el-radio-group v-model="form.resource">
<el-radio label="线上品牌商赞助"></el-radio>
<el-radio label="线下场地免费"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="活动形式">
<el-input type="textarea" v-model="form.desc"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">立即创建</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
<script>
export default {
data() {
return {
form: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
}
}
},
methods: {
onSubmit() {
console.log('submit!');
}
}
}
</script>

假设我们还需要为这个表单增加审批流程,例如文档中的这个表单

在加入新的表单项后,可能目前看着还好,但是随着表单项越来越多,这个文件会变得越来越大,越来越难以维护。所以我们尝试将这个表单项拆分为单个组件,模拟我们维护一个超大表单的场景。

新增子表单项组件

我习惯在当前表单的目录下,创建一个components目录,然后在components目录下创建一个audit目录,并在audit目录下创建一个index.vue文件,用于存放审批流程相关的组件。如果后续有一些只有audit/index.vue文件中才用到的组件,我也会放在audit目录下。保持目录结构清晰。

<template>
<div class="audit-form-item">
<el-form-item label="审批人" :prop="`${propPrefix}.user`">
<el-input v-model="form.user" placeholder="审批人"></el-input>
</el-form-item>
<el-form-item label="活动区域" :prop="`${propPrefix}.region`">
<el-select v-model="form.region" placeholder="活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
</div>
</template>
<script>
export const auditFormData = () => ({
user: '',
region: ''
}) export default {
name: 'auditFormItem',
props: {
value: {
type: Object,
default: () => auditFormData()
},
propPrefix: {
type: String,
default: ''
}
},
data() {
return {
form: this.value
}
},
watch: {
value(newVal) {
this.form = newVal
},
form(newVal) {
this.$emit('input', newVal)
}
}
}
</script>

因为element-ui在对表单进行校验时,实际上是对model上绑定的数据进行校验,所以为了能够对数据正确执行校验,我们需要在auditFormItem组件中实现v-model指令。

auditFormItem组件的propPrefix属性用于指定表单项的前缀,便于我们在嵌入到el-form中时,能够正确绑定表单项的prop属性。

auditFormData函数返回了当前表单项的默认数据。父组件通过执行该函数,可以对子表单执行正确的初始化。不仅如此,通过这种方式,我们将每个子表单项的数据和组件绑定在一起,避免了父组件data中出现大量表单项数据,导致难以维护的问题。每个子表单维护各自的数据,互不干扰。

如何嵌入已有项目

接下来我们尝试将auditFormItem组件嵌入到myForm.vue文件中

<template>
<el-form ref="form" :model="form" label-width="140px">
<!-- 其他表单项 -->
<!-- ... -->
<audit-form-item v-model="form.audit" propPrefix="audit"></audit-form-item>
</el-form>
</template>
<script>
import auditFormItem, { auditFormData } from './components/audit/index.vue'
export default {
components: {
auditFormItem
},
data() {
return {
form: {
audit: auditFormData()
}
}
}
}
</script>

如何进行校验

经过上面的操作,我们实现了将一个表单拆分为多个子表单项,那么如何进行表单校验呢?

我们知道在element-ui中,要对一个表单项进行校验有两种方式:

一种是在el-form上绑定rules属性,它会通过prop进行索引,自动对绑定的表单项进行校验。

另一种是在el-form-item上绑定rules属性,这会对单条表单项进行校验。

出于我们拆分表单项的场景,我们选择第二种方式,在el-form-item上绑定rules属性,然后在各个子组件中维护rules。如果有一些通用的校验规则,我们也可以在audit/validate.js文件中进行维护,然后通过import的方式引入。

如何处理联动校验

在复杂表单中,我们可能需要对多个表单项进行联动校验,例如:实时校验表单项的合法性,当form.regionshanghai时,form.user不能为空,当form.regionbeijing时,form.user必须为空。

那么如何处理这种联动校验呢?

我们可以在el-form所在的组件中,定义一个validate方法,通过element-ui提供的validateField方法,对特定进行校验。

<audit-form-item v-model="form.audit" propPrefix="audit" @validate="validate"></audit-form-item>

methods: {
validate(fields) {
this.$refs.form.validateField(fields)
}
}

然后在audit/index.vue文件中,我们可以通过$emit('validate', fields)方法,对当前表单项进行校验。

如何处理跨组件的联动校验

在我们拆分表单为多个子组件后,还可能会出现不同子组件之间的联动校验,例如:当子组件1中的form.regionshanghai时,子组件2中的form.user不能为空。

对于这个问题,其实我目前还没有想到很好的解决方案,当前是在el-form所在的组件中,定义额外的校验规则,然后绑定到一个空白的el-form-item上。

<el-form-item label="" label-width="0" prop="_form_validate_"></el-form-item>

rules: {
_form_validate_: {
validator: (rule, value, callback) => {
// 联动校验逻辑
}
}
}

也可以考虑用vuex来维护需要跨组件共享的数据。

const crossCmpConfig = {
state: {
region: '',
},
mutations: {
UPDATE: (state, { key, val }) => {
state[key] = val
},
},
} export default crossCmpConfig

多层嵌套

基于上面的方式,很容易就能想到,我们甚至可以继续在audit/index.vue文件中,继续嵌入别的子组件,例如:audit/audit-info/index.vue, 然后通过相同的方式,继续嵌入到audit/index.vue文件中。

结语

上面就是我日常开发中,处理复杂表单的一些经验总结,希望对大家有所帮助。

我是如何使用 vue2+element-ui 处理负责表单,避免单文件过大的问题的更多相关文章

  1. vue2.0+Element UI 实现动态表单(点击按钮增删一排输入框)

    对于动态增减表单项,Element UI 官方文档表单那一节已经介绍得很清楚了,我之前没有看见,绕了很多弯路,这里针对点击按钮增删一排输入框的问题做一个总结. 效果图如下 存在一排必填的姓名与手机号, ...

  2. Element ui 中的表单提交按钮多次点击bug修复

  3. vue 使用 element ui动态添加表单

    html部分 <div class="hello"> <el-form :model="dynamicValidateForm" ref=&q ...

  4. element ui动态生成表单数据与校验(后台传入数据)

    前言 最近有一个需求是通过后台返回的数据,生成表单并添加校验.在做的过程中,动态表单挺好做,关键是校验.困扰了我2天,最后通过查找资料和"运气"终于解决了.解决问题关键点:vue的 ...

  5. element ui form表单清空规则

    公司项目重构,经过商定使用element ui.在重构项目的时候发现一下element ui上很蛋疼的东西. 例如,这个form表单就是一个.趁着在高铁上没事,把想写的东西写一下. 先说一下eleme ...

  6. vue+element ui中select组件选择失效问题原因与解决方法

    codejing 2020-07-10 09:13:31  652  收藏 分类专栏: Web Vue Element UI 版权 .当表单form赋完值后,如果后续又对form中某一属性值进行操作如 ...

  7. vue2.0 + Element UI + axios实现表格分页

    注:本文分页组件用原生 html + css 实现,element-ui里有专门的分页组件可以不用自己写,详情见另一篇博客:https://www.cnblogs.com/zdd2017/p/1115 ...

  8. vue2.0 + element ui 实现表格穿梭框

    element ui 官网里介绍了穿梭框(Transfer),但在实际使用过程中,会出现一些问题: 1.穿梭框里能放置的内容太少,不能满足复杂的业务需求. 2.当选项过多时,穿梭框很难实现分页,左右两 ...

  9. vue2.0+Element UI 表格前端分页和后端分页

    之前写过一篇博客,当时对element ui框架还不太了解,分页组件用 html + css 自己写的,比较麻烦,而且只提到了后端分页 (见 https://www.cnblogs.com/zdd20 ...

  10. vue3的学习笔记:MVC、Vue3概要、模板、数据绑定、用Vue3 + element ui、react框架实现购物车案例

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.Vue是框架而jQuery则是库. 1.2.MVC(Mo ...

随机推荐

  1. golang模拟键盘输入字符串

    介绍 仅供学习使用哈,不要用来开gua. 代码仓库:https://github.com/GuoFlight/gkeybd (本人仓库,欢迎留言) 注意事项 只支持英文 使用前请切换到英文输入法.因为 ...

  2. 如何在Spring Boot框架下实现高效的Excel服务端导入导出?

    前言 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置 ...

  3. I2S 总线学习:1-有关概念

    背景 I2S总线 是一种常见的总线,也是需要掌握的. 概念 I2S(Inter-IC Sound)总线, 又称 集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准, ...

  4. 【资料分享】Xilinx Zynq-7010/7020工业评估板规格书(双核ARM Cortex-A9 + FPGA,主频766MHz)

    1 评估板简介 创龙科技TLZ7x-EasyEVM是一款基于Xilinx Zynq-7000系列XC7Z010/XC7Z020高性能低功耗处理器设计的异构多核SoC评估板,处理器集成PS端双核ARM ...

  5. 详解Web应用安全系列(8)不足的日志记录和监控

    在Web安全领域,不足的日志记录和监控是一个重要的安全隐患,它可能导致攻击者能够更隐蔽地进行攻击,同时增加了攻击被检测和响应的难度.以下是对Web攻击中不足的日志记录和监控漏洞的详细介绍. 一.日志记 ...

  6. 搜索Python编程获取相关图书信息

    1.获取相关图书信息 #搜索"Python编程"获取相关图书信息 from selenium import webdriver from selenium.webdriver.su ...

  7. 全网最适合入门的面向对象编程教程:13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用

    全网最适合入门的面向对象编程教程:13 类和对象的 Python 实现-可视化阅读代码神器 Sourcetrail 的安装使用 摘要: 本文主要介绍了可视化阅读代码神器Sourcetrail的安装与使 ...

  8. 用 Git 操作的数据库?这个项目火了!

    # 用 Git 操作的数据库?这个项目火了!> 超级特别又实用的数据库,快来补课!Git 是一个开源的分布式版本控制系统,可以敏捷高效地管理代码,让项目代码支持同时存在多个不同的版本和分支,是程 ...

  9. django 设置外键的时候,related_name的值大写还是小写,规则怎样

    django 设置外键的时候,related_name的值大写还是小写,规则怎样 在Django中,related_name参数用于定义反向关系的名称,即通过外键字段反向查询关联模型的对象.relat ...

  10. PixiJS源码分析系列:第二章 渲染在哪里开始?

    第二章 渲染在哪里开始? 牢记,按第一章介绍的 npm start 启动本地调式环境才可进行调式 如果是 example 文件夹内的例子还需要 serve . 开启本地静态服务器 上一章介绍了 Pix ...