目录
  • 前言
  • 起步 
  • 使用
    • update-form && getFormValue
    • 表单项动态显示或隐藏(hidden)
    • 表单数据联动(on)
    • 输入/输出格式化(inputFormat/outputFormat)
    • set-options
  • el-form-renderer 实践案例
    • 案例一
    • 案例二
  • 自定义组件接入指南

    前言

    el-form-renderer是基于element的表单渲染器,动态渲染,数据驱动

    el-form-renderer/README-zh.md at dev · FEMessage/el-form-renderer · GitHub

    el-form-renderer是基于 element-ui 封装的表单渲染器,但不局限于 element-ui 组件。在完整继承了 element 的form表单属性的基础上进行了简单扩展,一些非表单组件或者封装的自定义组件,如图片上传、富文本等也可进行整合,从而用户能够通过使用一段预设的数据渲染出一个完整的表单。

    el-form-renderer使用教程

    起步 

    ​# Step1 确认你已经正确安装并使用了 element-ui
    yarn add @femessage/el-form-renderer
    <template>
      <el-form-renderer :content="content"></el-form-renderer>
    </template>
    <script>
      import ElFormRenderer from '@femessage/el-form-renderer'
      export default {
        components: {
          ElFormRenderer,
        },
        data() {
          return {
            content: [],
          }
        },
      }
    </script>

    使用

    支持 el-form 上的所有属性。

    • content:[ObjectArray] 定义表单的内容,每一个 Object 代表一个原子表单 el-input, el-select, …,一切 el-form-item 上的属性都在此声明,而对于 el-input 等之上的属性在 $el 属性上进行声明,该 Object 上还存在其他属性,例如: id, type,label, options可选的,等。还有hidden定义其是否隐藏等属性
    • id: id: string  每一个原子都存在 id,用于存储该原子的值,不能重复
    • type: string  可以是element提供的所有表单组件类型,如传入'input',则渲染出'el-input,当type="group"时使用 items内依然遵循同一层级的id不重复的原则
    • readonly 只读的   当 type === 'input' 时展示文本值, 当 type === 'select' 时展示对应。 label 对于其他组件等同于 disabled = true
    • default: 默认值
    • options ({label: string; value?: any}[])具有选择功能的原子表单可用此定义可选项 select, radio-group, radio-button, checkbox-group, checkbox-button
    • hidden 传入一个方法,并返回 boolean,返回 true 时则隐藏该表单项 * formValue 为当前 form 值,item 为当前表单项的定义
    • el  用于定义具体原子表单(如el-input)的属性,比如定义el-input的placeholder
    • component   component适用于渲染局部注册组件和自定义组件,而type适用于带el-前缀的全局组件
    • label 设置el表单项的标签

    update-form && getFormValue

    • update-form 更新表单方法  默认情况下,updateForm 来者不拒,不在表单设置内的值,也可以存储进去
    • getFormValue 默认情况下,通过 updateForm 设置的所有值都会输出。 如果只想输出根据 content 设置的表单项的值,可传入 {strict: true}
    <template>
      <div class="update-form">
        <el-form-renderer :content="content" inline ref="formRender">
          <el-button @click="setValue">更新表单</el-button>
          <div>
            <el-button type="primary" @click="getValue(false)">获取数据</el-button>
            <el-button type="primary" @click="getValue(true)"
              >获取数据过滤掉字段</el-button
            >
          </div>
        </el-form-renderer>
        <pre>{{ value }}</pre>
      </div>
    </template>
    <script>
    export default {
      name: "update-form",
      data() {
        return {
          value: {},
          content: [
            {
              id: "name",
              type: "input",
              label: "name",
              el: {
                placeholder: "name",
              },
            },
            {
              id: "area",
              type: "select",
              label: "area",
              el: {
                placeholder: "area",
              },
              options: [
                {
                  label: "shanghai",
                  value: "shanghai",
                },
                {
                  label: "beijing",
                  value: "beijing",
                },
              ],
            },
          ],
        };
      },
      methods: {
        getValue(strict) {
          const value = this.$refs.formRender.getFormValue({ strict });
          this.value = value;
        },
        setValue() {
          this.$refs.formRender.updateForm({
            name: "alvin",
            area: "shanghai",
            // 设置冗余字段
            extraKey: "extraValue",
          });
        },
      },
    };
    </script>

    表单项动态显示或隐藏(hidden)

    以通过 hidden 控制某一表单项的显示或隐藏。

    <template>
      <div>
        <el-form-renderer :content="content"></el-form-renderer>
      </div>
    </template>
    <script>
    import ElFormRenderer from "@femessage/el-form-renderer";
    export default {
      components: {
        ElFormRenderer,
      },
      data() {
        return {
          content: [
            {
              type: "select",
              id: "selected",
              label: "选择项目",
              options: [
                {
                  label: "项目A",
                  value: "optionA",
                },
                {
                  label: "项目B",
                  value: "optionB",
                },
              ],
            },
            {
              label: "资料",
              type: "input",
              id: "data",
              el: {
                placeholder: "项目B的具体内容",
              },
              hidden: (form, item) => {
                return this.hiddenChange(form, item);
              },
            },
          ],
        };
      },
      methods: {
        hiddenChange(form, item) {
          console.log(form); //form 收集的数据
          console.log(item); //触发元素
          return form.selected !== "optionB";
        },
      },
    };
    </script>

    表单数据联动(on)

    可以通过 on 来监听 blur , focus 等事件来实现表单联动 监听表单项发出的事件
    <template>
      <div>
        <el-form-renderer :content="content"></el-form-renderer>
      </div>
    </template>
    <script>
    import ElFormRenderer from "@femessage/el-form-renderer";
    export default {
      components: {
        ElFormRenderer,
      },
      data() {
        return {
          content: [
            {
              label: "英文名",
              type: "input",
              id: "fullName",
              on: {
                blur: ([event], updateForm) => {
                  const value = event.target.value;
                  const lastName = value.split(" ")[1]; // 通过空格分割出内容
                  updateForm({ lastName }); // 更新其他表单项
                },
              },
            },
            {
              label: "姓氏",
              type: "input",
              id: "lastName",
            },
          ],
        };
      },
    };
    </script>

    输入/输出格式化(inputFormat/outputFormat)

    拿 日期范围选择器 为例,组件输出的值是一条字符串,但后端接口格式是两个字段 {startDate, endDate},则此时需要对数据进行格式化处理

    inputFormat 转换输入的数据, 使其变成表单项需要的数据格式

    <template>
      <el-form-renderer :content="content" ref="form" />
    </template>
    <script>
    export default {
      data() {
        return {
          content: [
            {
              el: {
                type: 'daterange',
                placeholder: '选择日期',
                valueFormat: 'yyyy-MM-dd'
              },
              type: 'date-picker',
              id: 'date',
              label: '日期',
              // 接口设计的时间范围是两个字段 '2019-07-23','2019-07-24'
              // 处理后的值为 [ '2019-07-23', '2019-07-24' ]
              inputFormat: row => ([row.startDate, row.endDate])
            }
          ]
        }
      }
    }
    </script>

    outputFormat 转换输出的数据, 使其变成需要的(接口期望的)数据格式

    <script>
    export default {
      data() {
        return {
          content: [
            {
              el: {
                type: 'daterange',
                placeholder: '选择日期',
                valueFormat: 'yyyy-MM-dd'
              },
              type: 'date-picker',
              id: 'date',
              label: '日期', 
              // 处理前的值为 date: [ '2019-07-23', '2019-07-24' ]
              // 处理后的值为 {startDate: '2019-07-23', endDate: '2019-07-24'}
              outputFormat: val => {
                if (!val) {
                  return {startDate: '', endDate: ''}
                }
                return {
                  startDate: val[0],
                  endDate: val[1]
                }
              }
            }
          ]
        }
      }
    }
    </script>

    set-options

    使用setOptions更新选择选项

    <template>
      <el-form-renderer ref="form" :content="content" inline>
        <el-button @click="setOptions">更新options</el-button>
      </el-form-renderer>
    </template>
    <script>
    export default {
      name: "select-demo",
      data() {
        return {
          content: [
            {
              id: "area",
              type: "select",
              label: "select",
              el: {
                placeholder: "select",
              },
              options: [
                {
                  label: "shanghai",
                  value: "shanghai",
                },
                {
                  label: "beijing",
                  value: "beijing",
                },
              ],
            },
          ],
        };
      },
      methods: {
        setOptions() {
          this.$refs.form.setOptions("area", [
            {
              label: "guangzhou",
              value: "guangzhou",
            },
            {
              label: "hangzhou",
              value: "hangzhou",
            },
          ]);
        },
      },
    };
    </script>

    el-form-renderer 实践案例

    案例一

    A 系统有一个解析简历的功能,后端接口只能解析电话、邮箱,也即接口只返回 phone、email 两个字段。后来接口更新了,支持解析姓名:

    后端:简历解析接口更新了,现在会返回多一个字段 name,你前端那边也更新一下吧。 前端:您随便加,接口直接更新就行了,前端不用改。 后端:这么神奇的吗?这是怎么做到的?

    el-form-renderer使用教程

     那么前端是如何做到接口返回多一个字段,自己却不用修改代码的呢?

    分析

    原因在于使用了 el-form-renderer 使用了 updateForm 来更新表单值。 updateForm 方法接受一个对象,只要传入对象的 key 与表单的 id 对应上即可更新数据。代码片段如下:

    <template>
      <el-form-renderer :content="content" ref="form" />
    </template>
    <script>
    export default {
      data() {
        return {
          content: [
            {
              type: 'input',
              id: 'name',
              label: '名称'
            },
            {
              type: 'input',
              id: 'phone',
              label: '电话'
            },
            {
              type: 'input',
              id: 'email',
              label: '邮箱'
            },
            // ...
          ],
        }
      },
      methods: {
        async fetch() {
          const data = await fetchData()  // data: Object
          // data 中返回多了一个字段 name,也不需要修改代码
          this.$refs.form.updateForm(data)
        }
      }
    }
    </script>

    所以,即使后端丰富了这个 data ,前端也可以“照吃不误”

    如果直接使用 el-form 则无法完成这种操作:你需要手动去更新每个与 el-form-item 绑定的 data 值

    <template>
      <el-form ref="form" :model="form">
        <el-form-item label="名称">
            <el-input v-model="form.name"></el-input>
        </el-form-item>
      </el-form>
    </template>
    <script>
    export default {
      data() {
        return {
          form: {
            // 每一个表单项需要主动绑定
            name: '',
            phone: '',
            email: '',
          },
        }
      },
      methods: {
        async fetch() {
          const {name} = await fetchData()  // data: Object
          this.form.phone = data.phone
          this.form.email = data.email
          // data 中返回多了一个字段 name,需要多写下面一行代码
          this.form.name = name
        }
      }
    }
    </script>

    案例二

    场景

    B 系统的表单页面比较多,其中不乏带有复杂组件的表单,如下图红框片所示:

    el-form-renderer使用教程

    直接使用 el-form 开撸,整个页面耦合在一起代码超过 1000 行。

    使用 el-form-renderer 后,通过拆分组件,整个页面代码量在 300 行左右,业务组件代码量在 100~300 行之间。

    明显能感觉到页面简洁了许多,维护性大大提高。

    那么,el-rorm-renderer 是怎么做到精简主页面代码的呢?

    分析

    秘诀在于 el-form-renderer 支持通过 component 属性渲染自定义组件、在组件内部定义检验规则,提高了拆分页面的可能性。

    下面代码示例中,把选择优惠券的表格,抽离成了一个单独的组件。

    <!--表单主页面-->
    <template>
      <el-form-renderer :content="content" ref="form" />
    </template>
    <script>
    import SelectTableList from './select-table-list.vue'
    export default {
      data() {
        return {
          content: [
            // ...
            {
              id: 'selectedCoupon',
              // 渲染自定义 table 组件
              component: SelectTableList,  
              label: '选择优惠券'
            },
            // ...
          ],
        }
      }
    }
    </script>

    下面是自定义 table 组件示例代码。

    <!--自定义 table 组件示例代码-->
    <template>
      <div class="select-table-list">
        <el-button type="primary" size="small" @click="visible = true">选择</el-button>
        <el-table :data="selectedList" border></el-table>
        <!--
                省略一些代码
        -->
      </div>
    </template>
    <script>
    export default {
      name: 'select-table-list',
      // 自定义校验规则
      rules: [
        {
          required: true,
          message: '自定义组件的提醒消息'
        }
      ],
      props: ['value'],
      data() {
        return {
          visible: false,
          selectedList: []
        }
      },
      methods: {
        confirm() {
          const selectedVal = 'table选中的值'
          // 更新 value 值,这样 el-form-renderer 可以通过 getFormValue() 拿到该值
          this.$emit('input', selectedVal)
          this.visible = false
        }
      }
    }
    </script>

    自定义组件接入指南

    el-form-renderer 的 type 有限, 默认只能渲染普通的表单项, 假如现在要渲染一个上传组件, type 就不够用了, 那怎么办呢? 这时候 component 选项就派上用场了

    本文将介绍如何开发符合 el-form-renderer 接入标准的自定义组件, 实现对自定义组件的渲染

    自定义组件接入的关键是在组件内部实现 v-model

    建议在自定义组件上绑定 $attrs 和 $listeners

    el-form-renderer 对 v-model 的要求是:

    有一个 props 为 value对外触发 input 事件

    <template>
      <el-form-renderer :content="content" />
    </template>
    <script>
    import MyInput from "@/components/my-input.vue";
    export default {
      data() {
        return {
          content: [
            {
              component: MyInput,
              id: "myInput",
              label: "label",
              // 传入组件属性
              el: {
                placeholder: "请输入一个 title",
                // type: "submit", // submit button
                title: "这是一个标题", // custom defined props
              },
              // 传入组件事件
              on: {
                focus: ([event], updateForm) => {
                  console.log(event.target.value); // output: input value
                },
                customEvent: ([value, msg], updateForm) => {
                  console.log(msg); // output: 'message'
                },
              },
            },
            {
              id: "document",
              type: "input",
              el: {
                type: "textarea",
              },
            },
          ],
        };
      },
    };
    </script>
    <template>
      <div>
        <!-- 自定义组件 my-input -->
        <el-input
          :value="value"
          @input="onInput"
          v-bind="$attrs"
          v-on="$listeners"
        />
      </div>
    </template>
    <script>
    export default {
      props: {
        value: String,
        title: String,
      },
      watch: {
        value(value) {
          this.$emit("customEvent", value, "message");
        },
      },
      methods: {
        onInput(val) {
          this.$emit("input", "my-input: " + val);
        },
      },
    };
    </script>

    需要注意,on 中的 function 定义,组件 emit 事件的 payload 将以「数组」的方式,回调到第一个参数

    第二个参数为 updateForm 方法

     到此这篇关于el-form-renderer使用教程的文章就介绍到这了,更多相关el-form-renderer使用 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。