引言

在工作中我经常需要处理一些复杂、动态表单,但是随着需求不断迭代,我们也许会发现曾经两三百行的.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. 05-Python函数

    函数定义与调用 函数由以下几个部分组成: 函数名 函数参数 函数体 返回值 定义一个函数: def showMyName(name): #定义函数 print(name) showMyName(&qu ...

  2. 【资料分享】基于TI Sitara系列AM3352/AM3354/AM3359核心板规格书

    1 核心板简介 创龙科技SOM-TL335x-S是一款基于TI Sitara系列AM3352/AM3354/AM3359 ARM Cortex-A8高性能低功耗处理器设计的低成本工业级核心板,通过邮票 ...

  3. 效率工具RunFlow完全手册之进阶篇

    欢迎来到RunFlow手册的进阶篇,如果您还不了解RunFlow,建议先阅读我们的基础篇. (Solo 社区投稿) 搜索文件 按文件大小过滤,添加 len 参数,比如:len:1kb-2kb,len: ...

  4. 手动设置提示在此环境中不可导入Django

    手动设置提示在此环境中不可导入Django 环境参数添加manage.py中的代码'DJANGO_SETTINGS_MODULE', 'codeProject.settings'

  5. 【译】使 Visual Studio 更加可视化

    任何 Web.桌面或移动开发人员都经常使用图像.你可以从 C#.HTML.XAML.CSS.C++.VB.TypeScript 甚至代码注释中引用它们.有些图像是本地的,有些存在于线上或网络共享中,而 ...

  6. 使用 Node.js 和 Express 构建基本的 Web API

    使用 Node.js 和 Express 构建 Web API Web API Node.js 中的 http 模块 创建 Express 框架 Web 应用程序 Express 框架 Express ...

  7. Pytest Pytest源码分析

    Pytest源码分析 By:授客 QQ:1033553122 测试环境 pytest 5.4.3 测试脚本mytest.py #!/usr/bin/env python # -*- coding:ut ...

  8. DP进阶合集

    (ps:本集合为Star_F总结的dp进阶知识,持续更新~. 转载本文章需要联系我,否则视为侵权!!) 前置知识:线性dp,背包,树形dp,区间dp 内容预览: 状压dp 数位dp dp优化(前缀和, ...

  9. .NET 高性能缓冲队列实现 BufferQueue

    目录 前言 适用场景 功能说明 使用示例 BufferQueue 内部设计概述 Topic 的隔离 Partition 的设计 对并发的支持 Partition 的动态扩容 Segment 的回收机制 ...

  10. 优化 GitHub 体验的浏览器插件「GitHub 热点速览」

    上周,GitHub 有个"安全问题"--CFOR(Cross Fork Object Reference)冲上了热搜,该问题的表现是: 远程仓库的提交内容任何人可以访问,即使已被删 ...