作者 段国伟、汪曦
在控制台类 web 应用中表单是最常见的交互形式。用户在表单中填写信息点击提交就能完成对数据创建或者修改操作。
最开始前端开发人员根据业务模型和具体需求通过逐一编写或者声明实现表单中的各个字段测试通过之后发布上线。渐渐的开发人员开始把一些常用的方法抽象成表单库复用提升开发效率。但随着业务复杂度的增加和需求的不断演进对表单的展示形式和灵活程度要求也在不断提高现有的表单库只能解决部分问题开发者仍需花费大量的精力在更新表单字段或者开发新表单上。
那有没有一种方式既能让开发人员快速构建表单同时在后期又很少或者根本不需要开发人员介入来更新表单呢
此时表单设计器应运而生。表单设计器提供了可视化界面让非专业开发人员也能通过拖拽的方式所见即所得的构建业务所需表单。
目前很多开源的表单设计器实现在 UI 上都大同小异设计器的结构类似设计软件的布局。表单设计器一般为左中右三栏布局:
表单设计器的输出是一份描述表单字段的 JSON Schema表单设计完成后 JSON Schema 将直接存储到后端。表单发布后前端再根据 JSON Schema 渲染表单。表单中所有字段的信息都是存储在 Schema 中所以每次对表单的更新都是修改 Schema 中的内容无需传统的编译过程。借助表单设计器不但将开发人员从应对业务变更的频繁改动中解放出来同时大大提高了非专业开发人的生产力较少了沟通成本。
JSON Schema 是表单设计器和表单渲染组件之间沟通的语言。要理解表单设计器的核心首先要理解 Schema。在实际的项目中JSON Schema 一般比较复杂此处不做展开。本文的主题是表单设计器的实现原理主要关心的是如何提供一个可视化界面让用户能够快速生成 SchemaSchema 的详细格式将在后续文章中介绍这里先提供一个简化版本的定义
interface Schema { fields: Record<FieldKey, FieldSchema>; } interface FieldSchema { title: string; type: 'string' | 'object' | 'array' | 'number' | 'boolean'; component: string; componentProps: { [name: string]: any; }; }
众所周知表单由多个 input 控件组成input 控件包含多种形式如文本、数字、单选和多选等。Schema 中除了描述字段对应的是哪种类型的 input 外还需要描述控件的行为例如是否限制输入长度是否必填等。有了这些描述后表单渲染组件才能根据 Schema 渲染出符合预期的表单。
在上面的类型定义中
component
表示该字段用什么 input 组件渲染componentProps
表示传给组件的 props
用于控制组件的行为type
表示组件接受和期望返回的数据类型FieldKey
是字段在表单中的唯一标识用户侧不透出title
表示表单中字段对应的 label它的值用户可读。表单设计器的任务就是从零开始或者将已有的 JSON Schema 作为输入对 Schema 中的字段做添加、删除和更新操作最后输出 Schema。如果我们把表单设计器看成一个整体那它的功能可以用下图表示
整体来看表单就是对每个控件的操作进行组合组合的结果就是完整的 JSON Schema。
为了能够实现对表单字段的修改我们在表单设计器中提供了字段配置区域用户在配置区域中可以通过可视化方式定义字段属性而无需关心 Schema 的具体格式。表单设计器负责将配置值转化成 Schema同时也负责将 Schema 转化成配置值用来回显配置后的页面表单。
说明
配置区域其实也是一个表单每种类型的控件也都有自己特定的配置表单。
想要完成上述功能每种控件都需要实现两个方法toConfig
和 toSchema
。这里用一个公式来表示这两种方法和 Schema 的关系其中 configValue
用来给配置表单做回显。
FieldSchema => toConfig => configValue => toSchema => FieldSchema
厘清了上述思路之后我们再回到表单设计器的 UI 呈现上来。
左侧是设计器支持的控件列表根据上面的分析每个控件都需要提供控件名称、配置表单、toConfig
和 toSchema
这四个接口的实现。中间的 canvas 负责展示 Schema 中的控件同时需要处理用户的点击和拖拽事件。当用户点击 canvas 中的某个字段时右侧的配置区域需要找到对应的配置表单并渲染出来。
以上是表单设计器最核心的架构实现还有一些实现上需要考虑的细节如表单 Schema 定义解析等将在后续的文章中逐步阐述请大家持续关注。
全象云低代码平台的表单设计器是基于 [Formily]实现的。Formily 的灵活扩展能力和为业务而生的特性让我们钦佩感谢 Formily 团队的贡献希望我们后面也能为 Formily 贡献代码。