首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >表单联动解决方案探讨

表单联动解决方案探讨

原创
作者头像
暂七师黑管手
发布2020-06-02 19:51:21
2.9K0
发布2020-06-02 19:51:21
举报
文章被收录于专栏:进击的全栈进击的全栈

导语 身为一个前端开发,相信诸如“如果这一项选择了A,那一项就只能选B”的需求,大家一定遇到过。“表单联动”是前端开发中的经典场景。本文对于表单联动问题的解决方案进行一个简单的探讨。

表单依赖关系的建模

表单联动是前端经常面临的问题,联动实际上是一组表单项和表单项之间的依赖关系的集合。比如经典的“省-市-区”三级联动,就包含了“省”、“市”、“区”三个表单项,以及“市->省”和“区->市”。表单项的依赖关系可以抽象成若干if(省 === '广东省') { 市 in [‘广州市’, ‘深圳市’, ...] }的形式,即“市”依赖于“省”。(被依赖项也可以有多个,比如C依赖于B和A;而依赖项有多个的情况可以拆解为相互独立的依赖关系)

基于这样的关系,表单项之间的依赖关系其实就可以用有向图来解释。表单项是图中的各个节点,而依赖关系就是图中的有向弧。以经典的“省-市-区”关系来举例,建立如下的图模型:

省市区依赖关系
省市区依赖关系

可以看到,“省-市-区”的依赖关系是一个相对简单的模式,可以用“依赖链”来表示。根据依赖关系的复杂程度,“依赖图”可以简化成“依赖树”或者“依赖链”。

省市区-学校模型
省市区-学校模型

对于简单的依赖关系,只需要简单地监听被依赖项的变化,再更新依赖项的范围即可。比如(React DEMO):

<Select value={province} options={PROVINCE_OPTIONS} onChange={(v) => setProvice(v)} />
<Select value={city} options={getCityOptions(province)} onChange={(v) => setCity(v)} />
<Select value={district} options={getDistrictOptions(city)} onChange={(v) => setDistrict(v)} />

而当依赖关系变得更加复杂时,这种分布式管理的方式,会让依赖关系变得难以维护。比如下方的略显复杂依赖模型:

套餐包购买配置
套餐包购买配置

这一类比较复杂的依赖关系,没有办法简化成“链”或者“树”的形式。如果依然使用监听被依赖项的变化来更新表单,会产生重复更新的问题。如上图中的依赖关系,当“地域”更新之后,会同时触发“套餐包内容”和“可用区的变化”,最终导致“购买时长”触发了两次更新。

使用拓扑排序生成依赖更新序列

基于表单依赖关系是一个有向图,如果能够保证这个图中是一个有向无环图,我们就可以使用拓扑排序来生成一个表单项的更新序列。

简单介绍拓扑排序的思路,就是每次从当前图中找出入度为0(没有箭头指向)的点,压入队列,并从图中删除该节点,最终生成一个排序队列。以上文中的例子来看,我们就可以得到一个更新队列:['套餐包类型', '地域', '套餐包内容', '可用区', '购买时长']。(1、队列顺序并不唯一;2、因为笔者表示依赖关系的有向弧指向和典型的拓扑排序的要求相反,这里实际实现时是取了出度为0的点)

在有了这样一个更新队列之后,可以集中管理表单的依赖关系。在监听到表单项的更新之后,拷贝一个表单的草稿,按照更新队列的顺序更新草稿,再整体更新表单。比如(React DEMO):

import produce from 'immer';

// ...

const onChange = (key, newValue) => {
  const newValues = produce(values, draft => {
    // 更新草稿draft
  });
  setFormValues(newValues)
}

// ...

<Form value={values} onChange={onChange}>
{ // ... }
</Form>

确保有向无环图的成立

拓扑排序的成立条件之一是当前的图中不存在环。在依赖图中,如果出现了环形结构,就意味着依赖关系中存在循环依赖。大部分情况下,循环依赖的关系在需求阶段就可以发现并避免;但仍然有某些情况下,循环依赖会不那么容易被发现。比如下面的一段描述:

在选择“省份”后,“高校”一栏只能选择该省份的学校;在选择“高校”之后,“省份”一栏自动选取当前学校的所在省份。

在这里“省份”和“高校”之间就产生了一个循环依赖。产生这种问题的原因,就是表单在初始情况下的依赖关系不明确,各个表单项处在一个全部可选的状态。规避这种问题,就需要将表单项的依赖关系收敛到一个不存在环的状态,笔者想到的解决方案有两个:

  1. 在开始时明确指定依赖关系,存在依赖的表单项处于“全部不可选”的状态。只有当该表单项的所有依赖项都确定了取值之后,才能更改。
  2. 在开始时明确指定依赖关系,并给出一个表单的默认值。此时表单的状态成为状态机中的某个具体状态节点,就不会产生混沌不清的依赖关系。

后记

前端对于表单的解决方案已经有很多很好的实践。比如react-final-form等优秀的开源框架,能够帮助我们解决大部分表单需求。只是在遇到表单需求时,有时候并不能简单地一把梭,而是需要我们对问题进行一些简单的分析。首先确保当前需求明确、设计合理,之后再着手coding,才能达到事半功倍的效果。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 表单依赖关系的建模
  • 使用拓扑排序生成依赖更新序列
  • 确保有向无环图的成立
  • 后记
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档