TL;DR
- drip-form在0.9.0的alpha版支持了可视化配置联动的功能(仍在测试中)
- drip-form通过协议到代码的转换,尽可能降低常见联动配置的开发成本
- 探讨:JSON diff动态生成常见联动和校验
- drip form的后续更新:v0.9.0是v0最后一个版本,后续v0以维护为主并更新v0的文档。v1规划中。
- 联动或表单有更好的想法,欢迎讨论
前言
重要: 这篇文章不是表单联动实现的最佳方案,只是个人对联动的一些想法,欢迎探讨
表单联动是表单可视化配置的难点之一,如何以最少的代码实现尽可能多的联动功能感觉是一个悖论。
- 联动功能多,代码量会增加
我们可以对联动到代码的这层去做转化配置逻辑,减少代码的开发工作量。 - 联动协议转换联动代码 ,降低了代码编写工作,却带来了新的可视化平台配置上手成本和适配成本
表单联动的实现方式
表单联动实现大体分为三种方式:
- 纯代码开发
- 联动协议配置化
- 部分联动配置化、复杂联动代码开发
开发一个可视化配置联动的方案需要考虑三个成本:
- 不使用可视化配置联动的代码编写成本、
- 使用可视化配置表单联动带来的协议理解和配置成本
- 使用低代码平台的理解成本
本质上并没有一个最佳的方案,本文只是针对固定表单联动场景,提高搭建效率的探索和纯配置实现尽可能多的联动功能的思考
联动常见场景
在设计配置协议前,我们需要了解常见的表单联动场景
- 表单A控制表单B的数据
- 表单A控制表单B的样式
- 表单A控制表单B的展示隐藏
- 表单A控制表单B的校验
还有更复杂的联动可能还涉及表单的数据处理和转换逻辑,在涉及到复杂逻辑的时候,纯配置去搭建的联动的成本,可能没有配置+代码或纯代码实现来的快。所以这里不过多讨论
方案设计
实现分析
对上述几种场景进行分析,发现大部分的联动代码逻辑都可以通过编写if语句实现。
if(条件A){执行B}
复制代码
条件A
针对不同的数据类型,梳理常见的判断表达式以及对应类型的比较操作符
- 表单A 是string类型,且为某个值时
- 表单A 是number类型,大于某个值时
- 表单A 是boolean类型,为真时
- 表单A 是对象类型,包括某个属性时
- 表单A 是数组类型,包括某个值时
从上面例子判断表达式的组成:表单A的值、比较操作符、被比较值
以下是常见的数据类型对应的比较操作符
// nubmer 类型支持的操作符
type NumberOperator = '>=' | '<=' | '<' | '>' | '===' | '!=='
// string 类型支持的操作符
type StringOperator = '===' | '!==' | 'includes' | '-includes'
// boolean 类型支持的操作符
type BooleanOperator = 'true' | 'false'
// object 类型支持的操作符
type ObjectOperator = 'in' | '-in'
// array 类型支持的操作符
type ArrayOperator = 'includes' | '-includes'
复制代码
我们需要获取表单A的值,这个值对应的可能是表单数据(data)、表单的ui数据(uiSchema)、校验数据(dataSchema)。
现在有一个如下的get函数去获取表单的数据
//获取表单A的数据
get(A).data
//获取表单A的ui数据
get(A).uiSchema
//获取表单A的校验数据
get(A).dataSchema
复制代码
最终,条件A可以用下面代码实现
//如果表单A是string类型,并且为1时,执行B
if(get(A).data === '1'){执行B}
复制代码
执行B
条件A满足的时候,我们需要执行B。
在表单联动中,执行B的语句通常是需要设置表单B的数据、ui数据、校验数据
现有一个set函数可以设置表单B的数据
//设置表单B的数据
set(B,'data',value)
//设置表单的ui数据
set(B,'uiSchema',value)
//设置表单的校验数据
set(B,'dataSchema',value)
复制代码
最终,整个语句用下面代码实现
//如果表单A是string类型,并且为1时,设置表单B的值为2
if(get(A).data === '1'){set(B,'data',2)}
复制代码
协议定制
联动的纯配置化,是需要一个协议去描述上面的if逻辑。并通过一定的转换逻辑(比如:new Function
)将配置转换为代码。
{
//联动协议版本
version:string
//联动协议触发时机
trigger:{
//全局监听
event:'globalChange'
}
//监听后执行的动作
actions:[
{
//if语句
type:'controlFlow'
//if条件 (条件A)
condintion:[
{
/**
* 表单A的值
* 格式:"fieldKey getType property" "fieldKey getType"
* fieldkey: 需要获取的表单的fieldkey
* getType: 对应get的属性 data|uiSchema|dataSchema
* property: 可选 获取属性(用于处理对象)
*/
fieldKey1:string
operator:string
//被比较值
value2:unknown
//可能存在多个条件 条件之间的关系 或/且
logicOperator:'||'
}
]
//if条件触发的动作(执行B
effect:[
{
//设置表单数据
type:'set',
/**
* 格式:"fieldKey setType property" "fieldKey setType"
* fieldkey: 需要获取的表单的fieldkey
* setType: data|uiSchema|dataSchema
* property: 可选 获取属性
*/
fieldKey:string,
value:unknown
}
]
}
]
}
复制代码
协议定制的时候,需要考虑后续协议的扩展性。
- 可能会添加新的逻辑判断语句支持(比如else语句、switch case语句等)
action中添加type判断当前语句 - 可能存在嵌套语句(比如if的嵌套,子逻辑等)
语句中的effect添加type判断语句类型 - 联动的触发时机(比如组件加载,组件变动,依赖变动等)
添加trigger.event
判断
交互设计
参考outlook的邮件规则,最终设计如下
- 条件
判断的条件可能存在多个,所以使用或或者且按钮添加更多的条件。 - 结果
条件成立,可能执行多个动作。可以使用新增动作添加更多的触发场景 - if语句或其他语句可能存在多个
使用新增条件添加更多的联动逻辑
目前方案和思考
联动二期规划
- 目前联动一期 alpha版并没有实现获取ui配置和设置ui数据和校验数据
二期需要支持配置ui配置和校验配置
比如:选中表单A,设置表单B的任何样式,设置表单B的任何校验信息 - 支持自增模式下嵌套内容的配置
思考
-
是否有必要支持复杂联动的配置?
复杂联动的配置本质上是对js的语法配置化,对于平台新手用户来说,可能并不了解代码的底层实现,配置成本巨高。而对于开发来说,可能复杂的配置还没有手写几行联动代码更快。因此联动配置化需要固定的表单业务场景,复杂联动的配置化并不适合。在开发联动配置化前需要梳理当前业务域、支持域。
-
是否需要一键设置反转? 比如:表单A选中某个值时,表单B隐藏。
开启自动反转,则表单A未选中某个值时,表单B都是展示的 -
是否需要开启动作触发时机和次数(生命周期概念)
比如:表单A选中某个值时,表单B值为1
表单B的值后续是否可以更改?可以更改,则这个联动是一次性的。不可以更改则是永久的 -
如何降低联动配置成本
现在的交互设计有理解成本。对于不了解平台的新手,搭建仍然一脸懵。是否可以通过两次表单的JSON对比,自动生成联动逻辑
- 先拖拽没有触发联动时的表单样式
- 配置触发联动之后的表单样式
- 两次配置的JSON自动比对,自动生成联动语句
- 生成的联动信息提示用户确认