如何使用 vue2+element-ui 处理复杂表单,避免单文件过大的问题

引言

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

文章转载自:azoux

原文链接:https://www.cnblogs.com/azoux/p/18362757

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个简的示例代码,但需要注意的是,此示例代码仅用于参考目的,您需要根据自己的实际需求进行修改和优化。 首先,在Vue项目中安装Element UI库: ``` npm install element-ui -S ``` 然后,在您的Vue组件中引入Element UI的样式和组件: ```vue <template> <div class="login"> <el-form ref="loginForm" :model="form" label-width="80px"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username" auto-complete="off"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password" auto-complete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login">登录</el-button> </el-form-item> </el-form> </div> </template> <script> import { ElForm, ElFormItem, ElInput, ElButton } from 'element-ui'; export default { name: 'Login', components: { ElForm, ElFormItem, ElInput, ElButton, }, data() { return { form: { username: '', password: '', }, }; }, methods: { login() { this.$refs.loginForm.validate(valid => { if (valid) { // TODO: 发送登录请求 console.log('登录成功'); } }); }, }, }; </script> <style> .login { margin: 50px auto; max-width: 500px; } </style> ``` 在上面的代码中,我们使用Element UI的`el-form`、`el-form-item`、`el-input`和`el-button`组件来实现登录表单。当用户点击“登录”按钮时,我们调用`login`方法,该方法首先调用`validate`方法验证表单输入是否合法,如果合法则向服务器发送登录请求。 需要注意的是,在实际项目中,您还需要编写后端接口来处理登录请求,并且在登录成功后将用户的登录状态保存在本地或服务器端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值