weixin_39912163
2021-01-07 04:15 阅读 22

表单解析器 · 开发教程

本文描述的解析器,是一个能将form-generator导出的json表单,解析为一个真实表单的程序。 接下来的行文中使用【json表单】表示form-generator导出的json表单。

剧透:本文其实就是带大家阅读parser.vue源码,哈哈。

布局

json表单目前支持两种布局: colFormItem和rowFormItem

1.1 colFormItem布局

colFormItem布局(以el-input为例)对应的json形式如下:


{
    "__config__": {
      "label": "单行文本",
      "labelWidth": null,
      "showLabel": true,
      "changeTag": true,
      "tag": "el-input",
      "tagIcon": "input",
      "required": true,
      "layout": "colFormItem",
      "span": 12,
      "document": "https://element.eleme.cn/#/zh-CN/component/input",
      "regList": [{
        "pattern": "/^1(3|4|5|7|8|9)\\d{9}$/",
        "message": "手机号格式错误"
      }]
    },
    "__slot__": {
      "prepend": "",
      "append": ""
    },
    "__vModel__": "mobile",
    "placeholder": "请输入手机号",
    "style": {
      "width": "100%"
    },
    "clearable": true,
    "prefix-icon": "el-icon-mobile",
    "suffix-icon": "",
    "maxlength": 11,
    "show-word-limit": true,
    "readonly": false,
    "disabled": false
  }

colFormItem布局对应的目标实际代码如下 :


    <el-col :span="12">
      <el-form-item label="单行文本" prop="mobile">
        <el-input v-model="formData.mobile" placeholder="请输入手机号" :maxlength="11" show-word-limit clearable prefix-icon="el-icon-mobile" :style="{width: '100%'}"></el-input>
      </el-form-item>
    </el-col>

在这个json到xml的解析过程中,form-generator的parser使用jsx来完成


...
const layouts = {
  colFormItem(h, scheme) {
    const config = scheme.__config__
    const listeners = buildListeners.call(this, scheme)
    let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
    if (config.showLabel === false) labelWidth = '0'
    return (
      <el-col span="{config.span}">
        <el-form-item label-width="{labelWidth}" prop="{scheme.__vModel__}" label="{config.showLabel" config.label :>
          <render conf="{scheme}" on: listeners></render>
        </el-form-item>
      </el-col>
    )
  },
...
}

1.2 rowFormItem布局

rowFormItem布局对应的json形式如下:


  {
    "__config__": {
      "layout": "rowFormItem",
      "tagIcon": "row",
      "layoutTree": true,
      "document": "https://element.eleme.cn/#/zh-CN/component/layout#row-attributes",
      "span": 12,
      "formId": 104,
      "renderKey": 1594570310282,
      "componentName": "row104",
      "children": []
    },
    "type": "default",
    "justify": "start",
    "align": "top"
  }

对应的目标代码如下:


    <el-col :span="12">
      <el-row>
      </el-row>
    </el-col>

同样使用jsx来完成布局解析:


  rowFormItem(h, scheme) {
    let child = renderChildren.apply(this, arguments)
    if (scheme.type === 'flex') {
      child = <el-row type="{scheme.type}" justify="{scheme.justify}" align="{scheme.align}">
              {child}
            </el-row>
    }
    return (
      <el-col span="{scheme.span}">
        <el-row gutter="{scheme.gutter}">
          {child}
        </el-row>
      </el-col>
    )
  }

值得注意的是,json表单支持嵌套; 通过__config__.children记录嵌套关系。使用renderChildren递归解析。(目前仅对rowFormItem布局的children做解析)


function renderChildren(h, scheme) {
  const config = scheme.__config__
  if (!Array.isArray(config.children)) return null
  return renderFormItem.call(this, h, config.children)
}

完整的代码,请阅读parse源码,此链接中的版本并不算复杂。

数据和逻辑

传统的vue格式表单,我们可能需要写如下格式的js,完成element UI表单的数据和逻辑。


export default {
  data() {
    return {
      formData: {
        mobile: undefined,
        field103: undefined,
      },
      rules: {
        mobile: [{
          required: true,
          message: '请输入手机号',
          trigger: 'blur'
        }, {
          pattern: /^1(3|4|5|7|8|9)\d{9}$/,
          message: '手机号格式错误',
          trigger: 'blur'
        }],
        field103: [{
          required: true,
          message: '请输入密码',
          trigger: 'blur'
        }],
      },
    }
  },
  methods: {
    submitForm() {
      this.$refs['elForm'].validate(valid => {
        if (!valid) return
        // TODO 提交表单
      })
    },
    resetForm() {
      this.$refs['elForm'].resetFields()
    },
  }
}

对于解析器来说,这是一个抽象的过程: - 数据部分: 构建表单数据实现如下:


  data() {
    const data = {
      formConfCopy: deepClone(this.formConf),
      [this.formConf.formModel]: {},
      [this.formConf.formRules]: {}
    }
    this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
    this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
    return data
  },
  • 逻辑部分: 请阅读,源码 methods 部分。这块和你日常vue编程差不多,只不过属性都是抽象的。

JSON表单结构说明

上边的一系列操作,都是建立在理解json表单都有哪些内容的基础上的。详细请参阅JSON参数对照表

form-generator中的render.js

render.js就是对vue的render函数的简单定制封装。如果你还不理解vue的render函数,请移步至:渲染函数 & JSX
render.js实现的功能是将json表单中的__config__.tag解析为具体的vue组件; 其工作过程可以理解为以下3个部分:


  render(h) {
    const dataObject = makeDataObject()
    const confClone = deepClone(this.conf)
    const children = []

    // 1 如果slots文件夹存在与当前tag同名的文件,则执行文件中的代码
    mountSlotFlies.call(this, h, confClone, children)

    // 2 将字符串类型的事件,发送为消息
    emitEvents.call(this, confClone)

    // 3 将json表单配置转化为vue render可以识别的 “数据对象(dataObject)”
    buildDataObject.call(this, confClone, dataObject)

    return h(this.conf.__config__.tag, dataObject, children)
  }

更多细节:源码render/render.js

关于拓展和讨论

本项目仅仅是开了个表单渲染头;实际要根据需求的差异,要做不一样的定制。之所以在issue写文章,是希望各位能充分利用好下边的评论功能,大家友好探讨。

系列教程: 《表单设计器 · 开发教程》 《表单解析器 · 开发教程》 《vue代码生成器 · 开发教程》 《vue代码预览器 · 开发教程》

该提问来源于开源项目:JakHuang/form-generator

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

7条回答 默认 最新

  • weixin_39731623 weixin_39731623 2021-01-07 04:15

    怎么没有看到表单解析器的开发教程呢

    点赞 评论 复制链接分享
  • weixin_39960700 weixin_39960700 2021-01-07 04:15

    同问呢

    点赞 评论 复制链接分享
  • weixin_39732506 weixin_39732506 2021-01-07 04:15

    说好的表单解析开发教程呢, 大佬是不是忘记弄了

    点赞 评论 复制链接分享
  • weixin_39519769 weixin_39519769 2021-01-07 04:15

    怎么没有看到表单解析器的开发教程呢

    parser啊大哥 大佬开始就写了 这是parser的解说 看下router路由 可以直接用的 扩展的话稍微看下你就懂了不用人家说 并且 编辑的时候运行有单独的iframe引入 已经和这功能解耦了

    点赞 评论 复制链接分享
  • weixin_39592315 weixin_39592315 2021-01-07 04:15

    我在parser上通过异步下载json 回来进行解析成页面,所有的表单项都能出来,唯独是表单的校验功能不正常,有大佬能正常校验的吗

    点赞 评论 复制链接分享
  • weixin_39761491 weixin_39761491 2021-01-07 04:15

    请问如果设计一个标签组件,内容是一个行容器,可行吗?想了挺久,没想到合适的解决方案,恳请各位大佬指点一下

    点赞 评论 复制链接分享
  • weixin_39778668 weixin_39778668 2021-01-07 04:15

    动态数据,除了在formConf 内直接构造外(即在formConf数据未到 parser 解析渲染前),还有其它方式可以动态构造吗

    点赞 评论 复制链接分享

相关推荐