经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
vue构建动态表单的方法示例
来源:jb51  时间:2018/9/25 19:02:07  对本文有异议

概述

后台管理系统里面有非常多的表单需求,我们希望能够通过写一个json格式的数据,通过vue的循环动态地去渲染动态表单。并且能够在外部得到渲染出来的表单的数据,可以对表单进行重置操作。我结合element ui的控件的下拉框,输入框,时间选择控件和vue-treeselect,做了一个动态表单。

v-model的理解

先简单讲一下vue-model是怎么玩的。其实vue-model相当于给表单元素传递一个value,外部监听input事件。所以我们自己封装表单组件的时候也是可以传递一个value值,监听input事件获取输入的值。

  1. <input type="text" v-model="something">
  2. <!--等价于-->
  3. <input type="text"
  4. v-bind:value="something"
  5. v-on:input="something = $event.target.value">

封装表单组件

组件最重要的开发思想就是设计好输入输出。这里就以下拉框组件为例吧。使用的是element ui的下拉框,进行一个简单封装。
输入:name:每个表单的数据标识,如区域编码输入框,父元素应该传递areaCode过来。

value: 表单选择/输入的值,从父元素获取后赋值给currentValue,通过监听父元素的值实现同步变
化。

options:下拉框要渲染的选项值,一般是个对象数组。

输出:onInputEvent,emit一个input事件,让父元素能够感知组件的数据变化。

也就是可以在组件使用的地方监听input事件

  1. <template>
  2. <el-form-item :label="label">
  3. <el-select v-model="currentValue" @input="onInputEvent">
  4. <el-option
  5. v-for="item in options"
  6. :key="item.value"
  7. :label="item.label"
  8. :value="item.value">
  9. </el-option>
  10. </el-select>
  11. </el-form-item>
  12. </template>
  13.  
  14. <script>
  15. import formMixins from '../../../mixins/form-model'
  16. export default {
  17. name: "SelectList",
  18. props: ['name', 'label', 'value''options'],
  19. mixins: [formMixins],
  20. data() {
  21. return {
  22. currentValue: this.value
  23. }
  24. },
  25. methods: {
  26. onInputEvent(value) {
  27. this.$emit('input', this.name, value);
  28. }
  29. },
  30. watch: {
  31. value(val) {
  32. this.currentValue= val;
  33. }
  34. }
  35. }
  36. </script>

一点封装

由于每个表单组件都是监听父元素的value值变化,数据变化时都是触发onInputEvent并执行this.$emit('input'),所以我们可以把这部分内容抽取出来放在mixins里面。

form-model.js

  1. export default {
  2. props: ['name', 'value'],
  3.  
  4. data () {
  5. return {
  6. currentValue: this.value
  7. };
  8. },
  9. methods: {
  10. onInputEvent(value) {
  11. this.$emit('input', this.name, value);
  12. },
  13. reset() {
  14. this.currentValue = "";
  15. }
  16. },
  17. watch: {
  18. value (val) {
  19. this.currentValue = val;
  20. }
  21. }
  22. };

然后我们的下拉框组件就可以少写一些共用的代码,直接用 mixins: [formMixins]

  1. <template>
  2. <el-form-item :label="label">
  3. <el-select v-model="currentValue" @input="onInputEvent">
  4. <el-option
  5. v-for="item in options"
  6. :key="item.value"
  7. :label="item.label"
  8. :value="item.value">
  9. </el-option>
  10. </el-select>
  11. </el-form-item>
  12. </template>
  13.  
  14. <script>
  15. import formMixins from '../../../mixins/form-model'
  16. export default {
  17. name: "SelectList",
  18. props: ['name', 'label', 'value', 'options'],
  19. mixins: [formMixins],
  20. data() {
  21. return {
  22. currentValue: this.value
  23. }
  24. }
  25. }
  26. </script>

动态生成表单

这里主要是根据配置的数据,循环生成表单组件。默认提供提交和重置按钮,如果不需要可以通过slot传递其他操作按钮。这里的要点主要有:

监听表单组件的数据变化:

每个表单组件都有一个name标识它的业务含义,绑定的数据也是formData[field.name],@input事件传递updateForm,在updateForm里面更新this.formData[name],保证了this.formData里面的数据是和表单组件选择/填写的内容一致。

重置时改变表单组件的数据:

因为组件内部会监听父元素的value,所以这里只要清空this.formData的值,组件内部的数据也会跟着清空。

  1. <template>
  2. <div>
  3. <el-form :inline="true" ref="form" :model="formData" class="demo-form-inline">
  4. <el-col :span="field.cols" v-for="(field, index) in config.fieldsConfig" v-bind:key="index">
  5. <component :key="index"
  6. :is="field.fieldType"
  7. :label="field.label"
  8. :value="formData[field.name]"
  9. :multiple="field.multiple"
  10. @input="updateForm"
  11. v-bind="field"
  12. :options="field.options"
  13. :ref="field.name"
  14. >
  15. </component>
  16. </el-col>
  17. <slot name="buttons">
  18. <el-button type="primary" @click="submit" size="small">{{onSubmitText}}</el-button>
  19. <el-button type="default" @click="reset" size="small">{{onResetText}}</el-button>
  20. </slot>
  21. </el-form>
  22. </div>
  23. </template>
  24. <script>
  25. import SelectList from './basicComponent/SelectList'
  26. import TextInput from './basicComponent/TextInput'
  27. import TimeSelector from './basicComponent/TimeSelector'
  28. import SelectTree from './basicComponent/SelectTree'
  29. import StaffSelectPopedit from './businessComponent/StaffSelectPopedit'
  30. export default {
  31. name: "FormGenerator",
  32. components: { SelectList, TextInput, TimeSelector, SelectTree, StaffSelectPopedit},
  33. props: ["config", "value"],
  34. data() {
  35. return {
  36. formData: this.value,
  37. onSubmitText: this.config.buttons.onSubmitText || '提交',
  38. onResetText: this.config.buttons.onResetText || '重置'
  39. }
  40. },
  41. methods: {
  42. updateForm(fieldName, value) {
  43. this.formData[fieldName] = value;
  44. },
  45. submit() {
  46. this.$emit("submit");
  47. },
  48. reset() {
  49. for(var name in this.formData) {
  50. if(typeof this.formData === "String") {
  51. this.formData[name] = "";
  52. } else {
  53. this.formData[name] = null;
  54. }
  55. }
  56. }
  57. }
  58. }
  59. </script>

业务使用的地方

像下拉框的选择数据,这些应该是后台渲染的,所以我们暂时用setTimeout模拟一下。感觉这里this.config.fieldsConfig[4].options写的不太优雅,依赖于配置数据的顺序肯定不是啥好事情。求大神指点。

  1. <template>
  2. <div>
  3. <form-generator :config="config"
  4. @submit="getFormData"
  5. v-model="formData"
  6. >
  7. </form-generator>
  8. </div>
  9. </template>
  10. <script>
  11. import FormGenerator from '../components/form/FormGenerator'
  12. export default {
  13. name: "FormGeneratorDemo",
  14. components: { FormGenerator },
  15. created () {
  16. this.queryOrderType();
  17. this.queryAreaTree();
  18. },
  19. data() {
  20. return {
  21. formData: {
  22. orderCode: "",
  23. orderType: "",
  24. beginTime: "",
  25. endTime: "",
  26. area: [],
  27. staff:""
  28. },
  29. config: {
  30. fieldsConfig: [
  31. {
  32. name: 'orderCode',
  33. label: '定单编码',
  34. fieldType: 'TextInput',
  35. cols: 8
  36. },
  37. {
  38. name: 'orderType',
  39. label: '定单类型',
  40. fieldType: 'SelectList',
  41. options: [],
  42. cols: 8
  43. },
  44. {
  45. name: 'beginTime',
  46. label: '开始时间',
  47. fieldType: 'TimeSelector',
  48. cols: 8
  49. },
  50. {
  51. name: 'endTime',
  52. label: '结束时间',
  53. fieldType: 'TimeSelector',
  54. cols: 8
  55. },
  56. {
  57. name: 'area',
  58. label: '区域',
  59. fieldType: 'selectTree',
  60. options: [],
  61. multiple: true,
  62. cols: 8
  63. },
  64. {
  65. name: 'staff',
  66. label: '人员选择',
  67. fieldType: 'StaffSelectPopedit',
  68. cols: 8
  69. }
  70. ],
  71. buttons: {
  72. onSubmitText: '提交',
  73. onResetText: '重置'
  74. }
  75. }
  76. }
  77. },
  78. methods: {
  79. getFormData() {
  80. console.log(this.formData);
  81. },
  82. queryOrderType() {
  83. setTimeout(() => {
  84. this.config.fieldsConfig[1].options = [
  85. { label: 'select1', value: 'key1'},
  86. { label: 'select2', value: 'key2'},
  87. { label: 'select3', value: 'key3'}
  88. ];
  89. }, 100)
  90. },
  91. queryAreaTree() {
  92. this.config.fieldsConfig[4].options = [
  93. {
  94. id: 'a',
  95. label: 'a',
  96. children: [{
  97. id: 'aa',
  98. label: 'AA',
  99. }, {
  100. id: 'ab',
  101. label: 'AB',
  102. }],
  103. }, {
  104. id: 'b',
  105. label: 'B',
  106. }, {
  107. id: 'c',
  108. label: 'C',
  109. }
  110. ]
  111. }
  112. }
  113. }
  114. </script>

大概就是这样的思路,我们希望我们只要写上面那样子的配置数据就可以动态生成各种这样的表单组件,不用写一大堆重复代码。如果有更好的解决办法,欢迎和我联系。另外,代码路径https://github.com/supportlss/vue-dynamic-form

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号