Vue规则表单组件的封装

这把我C 2020-12-05 14:15:53 9400
业务背景

业务场景是这样的,目前有一个表单,表单中28个表单项,但这些表单项只有三种类型,一种是输入框,一种是选择框,还有一种是输入域。每一个label都是一个可点击a标签,此外在label的后面,紧跟着一个图标,鼠标悬浮图标上会有气泡信息,显示该表单项的更多的信息。
页面的效果是长这个样子的

表单是基于Element-ui来完成的,按照常规的写法是这样做的,

<el-form ref="form" label-position="top" :model="jobContentForm" label-width="80px" >
  <p>
    <a href="https://docs.gitlab.com/ee/ci/yaml/README.html#job-keywords" target="_blank" >官方字段解释</a>
  </p>
  <el-form-item label="任务名">
    <el-input v-model="jobContentForm.name"></el-input>
  </el-form-item>

  <el-form-item label="镜像">
    <el-input v-model="jobContentForm.image"></el-input>
  </el-form-item>
  <el-form-item label="服务">
    <el-input v-model="jobContentForm.services"></el-input>
  </el-form-item>
  <el-form-item label="前置脚本">
    <el-input type="textarea" v-model="jobContentForm.before_script"></el-input>
  </el-form-item>
  <el-form-item label="后置脚本">
    <el-input type="textarea" v-model="jobContentForm.after_script"></el-input>
  </el-form-item>
  <el-form-item label="Runner tags">
    <el-input v-model="jobContentForm.tags"></el-input>
  </el-form-item>
  <!-- 此处还需要重复18个 el-form-item 标签-->
  </el-form>

这里的每一个el-form-item 都是一个表单项,一共有23个。
这种写法有个很大的缺点,那就是冗余代码太多,每一个项都要用el-form-item来包裹,而且代码非常的多,要修改一个表单也非常不好找要修改的数据。
做程序员就要敬精益求精,如果程序让你感觉不爽,那就优化它。

优化思路

于是乎,一段封装组件的历程由此开始。
解决这种冗余代码的难题有一个很明显的思路,那就是数据驱动,将代码压缩到极致。表单项由数据配置,组件的渲染也由数据驱动。这样实现下来,只需要一个el-input ,一个el-select, 修改配置数据也变得非常简单。

说干就干。

提取数据。

提取数据是一个技术活,它要求你要将事物抽丝剥茧,抽象起来。现在让我们一步一步来分析一下。
对照着页面,我们很容易想到每一个输入框,每一个选择框 都是一个对象,
这个对象会有

  • 唯一主键id,
  • 表单项的label,
  • 表单项的默认值 defaultValue
  • 表单项的类型type,type为input时,表明当前表单项为el-input, 为select时表单项为el-select
  • 点击label要到达的链接, link
  • label的详细解释 des
  • 表单项的 placeholder
  • 如果表单项是select时,需要有待选项 options这样一个数组

那么一个表单项的数据大致就是这样的

formItem = {
  id: 'image',
  label: '镜像',
  defaultValue: '',
  type: 'input',
  link: 'https://www.baidu.com/',
  des: 'Docker 镜像名',
  placeholder: '请输入',
  options:[{label:1,value:1},{label:2,value:2}]
},

提取出一个表单项后,举一反三,我们就能很简单地列举出其他表单项的配置上数据。只是改一下值而已。
一个表单中有多个对象, 这多个对象就组成了一个数组。这个数组就是一个表单的全部配置数据。
经过抽象提取,一个表单的数据大致是这个样子的

export const globaleForm = [
  { id: 'image', label: '镜像', defaultValue: '', type: 'input', link: 'https://www.baidu.com/', des: '12445' , placeholder: '请输入'},
  { id: 'services', label: '服务', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
  { id: 'before_script', label: '前置脚本', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
  { id: 'after_script', label: '后置脚本', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
  { id: 'tags', label: 'Runner标签', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
  { id: 'cache', label: '缓存', defaultValue: '', type: 'input', link: '', des: '', placeholder: '请输入' },
  { id: 'artifacts', label: '制品', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
  { id: 'retry', label: '重试次数', defaultValue: '', type: 'select', link: '', des: '' , placeholder: '请输入',
    options:[{label:1,value:1},{label:2,value:2},{label:3,value:3},]
  },
  { id: 'timeout', label: '超时时间', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
  { id: 'interruptible', label: '是否中断', defaultValue: '', type: 'input', link: '', des: '' , placeholder: '请输入'},
]

以上便是最终的配置数据。

提取组件

提取完表单的配置数据后,我们就要进行组件的封装,
由于是一个表单,这里肯定是少不了一个el-form 和 一个v-for去遍历globaleForm
创建一个 CustForom.vue 的组件,将表单的配置参数globaleForm传入。
内容如下

<template>
  <el-form :ref="formRef" label-position="top" label-width="80px" :model="formValue">
    <el-form-item :label="item.label" v-for="item in formItemArr" :key="item.id" >
      <!--TODO-->
    </el-form-item>
  </el-form>
</template>
<script>
export default {
  props: {
    // 表单的配置数据
    formItemArr: {
      type: Array,
      default: () => [],
    },
  }
};
</script>

v-for遍历的内部是组件的核心组件结构,由于我们的label是一个可以点击的,并且有tip气泡效果的,那么我们可以使用label的插槽来实现。
lebel这部分的代码就是这样子的,item是遍历的元素

<template slot="label">
  <a class="label-link-info" target="_blank" :href="item.link">
    <span class="label-text">{{item.label}}</span>
    <el-tooltip v-if="item.des" class="item" effect="dark" :content="item.des" placement="top">
      <i class="el-icon-info"></i>
    </el-tooltip>
  </a>
</template>

处理完label,下面就是输入框与选择框的逻辑判断了, 由于我们用属性type来表明当前的表单项是input 还是select,所有这里的逻辑很简单,
如果type是input,那么就渲染一个el-input, 如果是select没那么就渲染有一个el-select.,另外我们需要一个对象来存储用户输入的数据,命名为formValue, 并且需要将配置的表单数据中的默认值,赋值给formValue
表单输入区域的组件结构就变成这样了.分析到这一步 组件的代码就变成了以下这样子

<template>
<el-form :ref="formRef" label-position="top" label-width="80px" :model="formValue">
  <el-form-item :label="item.label" v-for="item in formItemArr" :key="item.id" >
    <!--label插槽的设置 -->
    <template slot="label">
      <a class="label-link-info" target="_blank" :href="item.link">
        <span class="label-text">{{item.label}}</span>
        <el-tooltip v-if="item.des" class="item" effect="dark" :content="item.des" placement="top">
          <i class="el-icon-info"></i>
        </el-tooltip>
      </a>
    </template>
    <!--输入框 输入域的渲染-->
    <el-input v-if="item.type === 'input'" :type="item.inputType" v-model="formValue[item.id]"></el-input>

    <!--选择框的渲染-->
    <el-select v-if="item.type === 'select'" v-model="formValue[item.id]" placeholder="请选择">
      <el-option v-for="oitem in item.options" :key="oitem.value" :label="oitem.label" :value="oitem.value">
      </el-option>
    </el-select>
    </el-form-item>
  </el-form>
</template>
<script>
export default {
  props: {
    formItemArr: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      formValue: {},
    };
  },
  watch: {
    // 监听formItemArr,将formValue的值设置上
    formItemArr: {
      handler(v) {
        if (!v.length) return;
        v.forEach((x) => {
          this.$set(this.formValue, x.id, x.defaultValue || "");
        });
      },
      immediate: true,
    },
  },
};
</script>

到了这一步我们已经完成了大部分,配置好数据,调试以下。只要在编写一个方法,将表单的值对外暴露一下即可。
这样写

  methods: {
    getFormValue() {
      return this.formValue
    },
  },

然后我们在组件调用的时候使用ref获取组件的实例,进而调取getFormValue()方法,就是简单地在父组件里调用子组件的方法从而获取到自组件内部的data数据.

写在最后

整个过程说复杂也不复杂,说简单也不简单,重要的是大家要掌握抽象组件,提取配置数据的思路。
注意,通过数据驱动编写的组件,扩展性并不是很好, 与不封装,单独调用底层的方式相比。如果你的表单,业务逻辑很复杂,并且还有很多动态元素。建议还是不用这种数据驱动的表单组件。

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 95 8 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
这把我C
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区