乐趣区

关于低代码:手撸一个拖拽式表单生成低代码工具leggo

  • 演示地址:https://kerrycodes.github.io/
  • Github:https://github.com/KerryCodes…
  • 装置:npm i leggo

story

因为老员工的到职,我接手了一个齐全由表单组成的公布模块,之前我并没有看过业务源码。后续因为接入新的需要而冲进去保护这坨代码。留神我用了“坨”这个量词,你应该明确我在说什么。因为整个模块短少顶层设计,导致保护老本极高,频频报 bug。于是开始思考为什么不能通过拖拽间接生成和保护这些表单呢?于是 leggo 就此诞生,名字来源于乐高 Lego,寓意像搭积木一样实现表单的设计。

leggo 哲学

  • 尽管利用了“拖拽”这一最直观简略的交互方式。但leggo 并非面向非前端人员,leggo 是一个为业余前端开发者晋升效率的工具
  • leggo 从一开始就没有想要做成一个大而全的表单配置解决方案。 第一版甚至只是花了一个下午便告实现。所以 leggo 并不会为了可能实现简单逻辑的表单组件而放弃易用性。leggo 真正要做的是帮忙前端开发疾速搭建和保护表单构造,轻松实现那些无脑反复搬砖的表单组件。这使得前端开发可能更专一于开发领有特定简单逻辑交互的表单组件。
  • leggo 放弃了简直齐全凋谢的自在拓展空间。 岂但可拖拽的表单组件库可能被自在注册。所有通过 leggo 模板渲染的组件也能够轻松的被整体笼罩、组装或重写属性。你甚至能够裸露各种公共状态和函数给 leggo 生产。所以 leggo 并非只能利用于简略的表单设计场景,这齐全取决于开发者的大胆拓展。
  • 上手老本极低。 如果你相熟 React 和 Antd,则你只须要学习 1 - 2 个 leggo 的 Api 就能够开始在我的项目中部署。Antd 库中 FormForm.Item 以及相干 input 组件的所有属性和事件仍旧能够失常定义和应用。

根底利用

以下所有代码的外围就只是这一句const leggo= LeggoForm.useLeggo(schemaModel, middleware, publicStates),心愿不要有任何浏览累赘。

假如你曾经通过 leggo 表单设计器生成了一个表单模板(取得一个 JSON 对象,即下列代码中schemaModel),则仅通过以下 2 行代码你就失去了理论须要的表单:

export const schemaModel= {//JSON}
export {LeggoForm} from 'leggo'
export {schemaModel} form './schemaModel'


function MyForm(){const leggo= LeggoForm.useLeggo(schemaModel)
  
  return <LeggoForm leggo={leggo} />
}

咱们从 leggo 导出了 LeggoForm,而schemaModel 是通过 leggo 表单设计器生成的 JSON,LeggoForm这个高阶组件会将模版 JSON 解析并渲染成理论的表单组件。

从应用的角度来说 LeggoForm 和 Antd 中的 Form 简直没有任何差异(除了必须要挂载 leggo 这个属性)。你能够在 LeggoForm 上定义任何 Form 组件容许的属性并失常运行,比方像以下这样:

function MyForm(){const leggo= LeggoForm.useLeggo(schemaModel)
  const [form]= Form.useForm()

  return (<LeggoForm leggo={leggo} 
      form={form} 
      labelCol={span: 6}
      wrapperCol={span: 14}
      onValuesChange={handleValuesChange} 
      onFinish={hanldeFinish} 
      .
      .
      .
    />
  )
}

思考到你的表单模板 schemaModel 可能不是存在本地,而须要从服务器异步近程拉取,则你能够这样部署:

function MyForm(){const leggo= LeggoForm.useLeggo()
  const [form]= Form.useForm()
  
  useEffect(() => {leggo.resetSchemaModel(schemaModel)
  }, [schemaModel])

  return (<LeggoForm leggo={leggo} 
      form={form} 
      labelCol={span: 6}
      wrapperCol={span: 14}
      onValuesChange={handleValuesChange} 
      onFinish={hanldeFinish} 
      .
      .
      .
    />
  )
}

简略的表单通过以上代码曾经足够部署利用了。上面咱们逐渐引入更简单的场景。以下是咱们须要的一个表单组件,能够留神到在组件到右侧有一个“同步”按钮。这种个性化的组件并没有方法通过拖拽实现全副设计。

不要焦急,咱们先通过拖拽实现左侧经典表单组件局部的设计。右侧的按钮咱们只须要在渲染表单前通过中间件函数注入即可,中间件函数在 leggo 中是作为表单渲染前灵便拓展的伎俩之一。代码如下:

import {TConfigs} from 'leggo/lib/interface';


function MyForm(){const leggo= LeggoForm.useLeggo(schemaModel, middleware)
  const [form]= Form.useForm()
  
  //  异步的状况
  //  useEffect(() => {//    leggo.resetSchemaModel(schemaModel, middleware)
  //  }, [schemaModel])

  return <LeggoForm leggo={leggo} />
}


// 定义一个中间件函数
function middleware(configs: TConfigs) {configs.Successor= (props: React.PropsWithChildren<any>) => (<div style={{display: 'flex'}}>
      {props.children}
      <Button> 同步 </Button>
    </div>
  )
}

值得注意的是,中间件函数只会在整个 schemaModel 注入时执行。无论表单后续如何更新和渲染,两头函数都不会运行(除非你又通过 leggo.resetSchemaModel 从新注入一个新的schemaModel)。所以你不须要放心由中间件函数可能导致的性能问题。

你会留神到 leggo 引擎在运行中间件函数时为其注入一个参数 configs。这个对象十分重要,它来自schemaModel,你通过 leggo 表单设计器拖拽和设置的简直所有参数都存在这个对象中。实际上,咱们正是通过中间件函数在革新由表单设计器生成的schemaModelconfigs 中有 2 个要害的属性 itemPropsinputProps

itemProps

itemProps 蕴含的参数有 name、label、rules 等。没错,这个对象会被间接注入进 Form.Item 组件:

<Form.Item {...itemProps} ></Form.Item>

inputProps

inputProps 蕴含的参数有 disabled、style、placeholder、htmlType 等。这个对象会被间接注入进 input 相干组件:

<Input {...inputProps} />

<Checkbox.Group {...inputProps} />

<DatePicker {...inputProps} />

而后你发现在表单渲染前,通过中间件函数你有足够的机会去拓展整个表单的设计,比方:

function middleware(configs: TConfigs) {const { itemProps, inputprops}= configs
  
  if(itemProps.name !== 'test'){return} // 筛选咱们须要革新的表单
  
  itemProps.label= 'test2' // 咱们更换了 label
  
  itemProps.options= [ // 咱们更换了选项类组件所须要的 options 数据
    {label: '浙江', value: 'zj'},
    {label: '福建', value: 'fj'},
  ]
  
  configs.Successor= (props: React.PropsWithChildren<any>) => (<div style={{display: 'flex'}}>
      {props.children}
      <Button> 同步 </Button> 
    </div>
  ) // 咱们在表单右侧减少了一个按钮
  
  configs.SuperSuccessor= () => (
    <Form.Item label="newOne" name="newOne">
      <Input />
    </Form.Item>
  ) // 咱们甚至用一个全新的表单组件齐全笼罩和抛弃了原有的表单组件
}

Successor 和 SuperSuccessor

对于简单的表单来说,SuccessorSuperSuccessor十分有用,它是开发者利用 leggo 专一于简单逻辑表单组件的要害。leggo 表单设计器中所谓的 占位表单 组件即是为 SuccessorSuperSuccessor 的替换操作预留了构造空间。

区别在于,Successor只是对原有的表单组件进行革新(原有的表单组件通过 children 的形式传给它,itemPropsinputprops 仍旧会注入)。而 SuperSuccessor 则十分激进的笼罩和抛弃了原有的表单组件(itemPropsinputprops 也被抛弃)。

<Form.Item {...itemProps}>
  <Successor>
    <Input {...inputprops} />
  </Successor>
</Form.Item> 
<SuperSuccessor />

以上是通过中间件函数一次性的革新 schemaModel。而如果你须要随时扭转表单的渲染形式,比方disabled 属性,则你能够在任意机会通过调用 leggo.updateSchema 来实现。每个表单组件是独自重渲染的,所以你也不须要思考性能的问题。如下:

// 能够随时调用
leggo.updateSchema('select', configs => {const { inputprops}= configs
  inputprops.disabled= true
})


// 中间件函数实现形式(留神这个函数只会运行一次!):function middleware(configs: TConfigs) {const { itemProps, inputprops}= configs
  if(itemProps.name === 'select'){inputprops.disabled= true}
}

高级利用

好的,让咱们进一步引入更简单的设计场景。假如咱们须要设计一个容许数据联动的表单,日期组件是否必选由“单选”这个组件的值决定。

则咱们只须要通过 leggo 表单设计器设置数据关联和简略的逻辑运算操作即可。

[{label: '必选日期', value: 1}, {label: '非必选日期', value: 2}]

如果咱们的关联值是在程序运行时能力拿到的,则咱们能够通过在表单理论渲染前传入一个公共状态即可。公共状态既能够是值,也能够是函数,leggo 引擎会自动识别。

还有一种常见的场景就是对于 SelectRadio等组件须要近程拉取 options 数据,通过 leggo 也同样能够很不便实现这样的需要。

带有关联按钮的数据都能够设置关联数据

公共状态引入形式如下:

const publicStates= {
  testvalue: 1084,
  testFunc: () => 'test',}

function MyForm(){const leggo= LeggoForm.useLeggo(schemaModel, middleware, publicStates)
  const [form]= Form.useForm()

  return <LeggoForm leggo={leggo} />
}

leggo 表单设计器

演示地址:https://kerrycodes.github.io/

如果你须要在我的项目中间接部署这个表单设计器,则能够间接引入 LeggoConfigs 组件,onGetSchemaModel属性会在 schemaModel 生成后主动注入和执行,款式须要手动引入。如下:

import 'leggo/lib/styles/configs.less'

<LeggoConfigs onGetSchemaModel={postSchemaModelData} />

function postSchemaModelData= (schemaModel: TSchemaModel) => {console.log('发送 schema~~~~~~', schemaModel)
}

重要!

我的项目地址:https://github.com/KerryCodes…

入行快一年了,编乎上没发过答复都攒了 20 个粉丝了,Github 的 star 仍旧挂 0!!!各位大佬能不能动动金手指,让我体验一下有 star 的快感~

退出移动版