FormRender 2.0 开箱即用表单方案,助你准点下班

FormRender 2.0 开箱即用表单方案,助你准点下班

首页模拟经营拖拖小镇穿越忙碌的世界2023更新时间:2024-06-17

官方文档:xrender.fun/form-render

github:https://github.com/alibaba/x-render

本文作者飞猪前端 @lhbxs,分享 FormRender 2.0 开箱即用的表单解决方案,产品体验更上一层楼

一、前言

在前端开发过程中,表单渲染是重要且繁琐的一环。为了提高开发效率并避免重复工作,飞猪推出了基于 React 的表单渲染器 FormRender。FormRender 使用 JsonSchema 协议渲染表单,是适用于中后台表单的一种通用解决方案。本文将介绍 FormRender 的基本概念、使用方式及高级特性。

二、简介

优势

三、如何使用
  1. 安装依赖

npm install form-render --save

  1. 使用方式

import React from 'react'; import FormRender, { useForm } from 'form-render'; const schema = { type: 'object', properties: { input: { title: '输入框', type: 'string' }, select: { title: '下拉框', type: 'string', props: { options: [ { label: '早', value: 'a' }, { label: '中', value: 'b' }, { label: '晚', value: 'c' } ] } } } }; export default () => { const form = useForm(); const onFinish = (formData) => { console.log('formData:', formData); }; return ( <FormRender form={form} schema={schema} onFinish={onFinish} footer={true} /> ); }

  1. 通过可视化表单生成器 Schemabuilder ,用户可以拖拖拽拽快速生成 JsonSchema。

4. 通过 Playground 可以进行在线体验,并查看其中丰富的表单示例。

四、高级特性1. 表单校验

FormRender 简化了表单校验配置,常规校验可以通过协议内置字段进行配置,同时也支持像 Antd Rules 的配置方式,并且可以处理更复杂的子表单联动校验配置。

内置校验

为了简化校验逻辑的配置,FormRender 内置了一些校验配置选项,包括以下字段:

const schema = { "type": "object", "displayType": "row", "properties": { "input1": { "title": "必填", "type": "string", "widget": "input", "required": true }, "input2": { "title": "数值大小", "type": "number", "widget": "inputNumber", "max": 5, "min": 1 }, "input4": { "title": "字符串长度", "type": "string", "widget": "input", "max": 20, "min": 5 }, "input6": { "title": "url 校验", "type": "string", "widget": "urlInput", "format": "url" } } } Rules 校验

FormRender 的 Rules 校验规则可以参考 Antd Form Rules API,validator 自定义校验规则做了一点小的调整,自定义方法的返回值类型,改成布尔值或者对象格式

const schema = { type: 'object', displayType: 'row', properties: { input2: { title: '自定义校验', type: 'string', rules: [ { validator: (_, value) => { const pattern = '^[\u4E00-\u9FA5] $'; const result = new RegExp(pattern).test(value); return result; // 或者是返回一个对象,用于动态设置 message 内容 // return { // status: result, // message: '请输入中文!', // } }, message: '请输入中文!' } ] } } }; 子表单校验

在处理非常复杂的表单场景时,有时会使用自定义组件作为子表单。在这种情况下,表单的提交行为无法触发子表单进行校验,因此需要对自定义子组件进行特殊处理。

子表单组件提供一个触发内部校验的函数,并通过 useImperativeHandle 暴露出去

import { useImperativeHandle } form 'react'; const ChildForm = (props) => { // 内部校验方法,异步校验请用 async、await 语法 const validator = async () => { return true; // 返回 boolean 值,true 表示内部校验通过 // 如果需在外部显示子表单错误信息可以使用对象形式返回 // retrun { status: boolean, message: string }; }; useImperativeHandle(props.addons.fieldRef, () => { // 将校验方法暴露出去,方便外部表单提交时,触发校验 return { validator }; }); return ( ...// 表单渲染代码 ); } export default ChildForm; 2. 表单联动

表单联动是前端开发中常见的一种需求,也是前端开发人员经常遇到的难点之一,往往评价一个表单渲染器能力强不强,表单联动能力至关重要。FormRender 通过函数表达式依赖项关联、watch 监听 等方式尽可能的在表单联动上做的更加易用,降低表单联动的成本。

函数表达式

函数表达式为字符串格式,以双花括号 {{ ... }}为语法特征,对于简单的联动提供一种简洁的配置方式。组件所有的 Schema 协议字段都支持函数表达式。

例如:控制表单项禁用、隐藏、文案提示等交互。

{ "type": "object", "properties": { "input": { "title": "输入框", "type": "string", "widget": "input", "hidden": "{{ formData.hidden }}", "disabled": "{{ formData.disabled }}", "placeholder": "{{ formData.placeholder || '请输入' }}" }, "placeholder": { "title": "修改提示信息", "type": "string", "widget": "input" }, "hidden": { "title": "隐藏", "type": "boolan", "widget": "switch" }, "disabled": { "title": "禁用", "type": "boolan", "widget": "switch" } } }

除此之外,FormRender 还支持更加细粒度的配置,例如使用函数表达式来控制某个选项的行为。

{ "type": "object", "properties": { "input1": { "title": "当输入框的值为 2 时,下拉单选第二个选项隐藏,如果已选中并清空选中值", "type": "string", "widget": "input" }, "select1": { "title": "下拉单选", "type": "string", "widget": "select", "defaultValue": "{{ (formData.input1 === '2' && formData.select1 === 'b') ? undefined : formData.select1 }}", "props": { "options": [ { "label": "早", "value": "a" }, { "label": "中", "value": "b", "hidden": "{{ formData.input1 === '2' }}" }, { "label": "晚", "value": "c" } ] } } } } watch 监听

Antd Form 通过 onValuesChange 事件来监听字段值的更新,而 FormRender 提供了一个功能更为强大的 API 来监听值的变化,即 watch API。下面让我们深入了解其用法。

const watch = { // '#' 等同于 onValuesChagne '#': (allValues, changedValues) => { console.log('表单 allValues:', allValues); console.log('表单 changedValues:', changedValues); }, 'input': value => { console.log('input:', value); }, // 监听 对象嵌套 场景,某个表单项的值发生变化 'obj.input': (value) => { console.log('input:', value); }, // 监听 List 场景,某个表单项的值发生变化 'list[].input': (value, indexList) => { console.log('list[].input:', value, ',indexList:', indexList); } };

配合 form.setSchemaByPath和 form.setValueByPath就能实现更加复杂的表单联动需求。

dependencies 关联依赖项

当依赖项的值发生变化时,组件自身会触发更新和校验操作。在组件内部,可以通过 props.addons.dependValues 属性来获取依赖项的更新值。

一个经典的场景就是“密码二次确认”,代码如下

const schema = { type: 'object', displayType: 'row', properties: { input1: { title: '密码', type: 'string', }, input2: { title: '确认密码', type: 'string', dependencies: ['input1'], rules: [ { validator: (_, value, { form }) => { if (!value || form.getValueByPath('input1') === value) { return true; } return false; }, message: '你输入的两个密码不匹配' } ] } } }; getFieldRef

在使用自定义组件时,可以通过 form.getFieldRef('path') 方法获取到对应的组件实例。其中,path 是该组件的表单路径,当然自定义组件也要通过 props.addons.fieldRef 操作进行绑定。

import { useImperativeHandle } form 'react' const CustomField = (props) => { useImperativeHandle(props.addons.fieldRef, () => { return { // 暴露内部方法 }; }); return ( ...// 组件渲染代码 ); } export default CustomField; 3. 数据转换

在开发过程中,经常会遇到需要将前端数据转换为符合服务端数据结构的情况。为了解决这个问题,FormRender 提供了一个名为 bind 的魔法字段。通过在表单项中指定 bind 字段,可以将该表单项的值绑定到一个自定义的数据结构中,从而方便实现前后端数据格式的转换。

场景一

表单收集到的日期数据结构是:{ "date": \["2023-04-01","2023-04-23"] },而服务端要求的数据结构是:{ "startDate": "2023-04-01", "endDate": "2023-04-23" }

const schema = { "type": "object", "properties": { "date": { "bind": [ "startDate", "endDate" ], "title": "日期", "type": "range", "format": "date", "description": "bind 转换" }, "date1": { "title": "日期", "type": "range", "format": "date", "description": "未进行转换" } } }

场景二

相信用过 FormRender 1.0 版的用户应该都清楚,使用 List 组件收集到的数据结构是:{ "list": [{ "size": 1 }, {"size": 2}]},而你希望的数据结构可能是 { "list": [1, 2] }, 2.0 已经完美支持。

const schema = { "type": "object", "properties": { "list": { "type": "array", "widget": "simpleList", "items": { "type": "object", "column": 3, "properties": { "input": { "bind": "root", "title": "大小", "type": "number" } } } } } }

更多关于 bind 字段的使用技巧,请前往官方文档进行阅读

4. 组件自定义

FormRender 提供了一些基础组件,例如 input、select 和 radio 等,但有时候这些组件并不能完全符合我们的业务需求,此时可以考虑使用自定义组件。FormRender 提供了丰富的自定义组件 API 和接口,以满足业务在表单组件上的个性化需求。

除此之外,FormRender 还支持自定义嵌套组件(object)和列表组件(list),所以真正意义上的自定义组件包括三种类型:输入控件、嵌套组件和列表组件。在使用自定义组件时,可以根据不同的类型使用相应的自定义 API 和接口对组件进行个性化调整和扩展。

输入控件自定义

在使用自定义输入控件时,只需要让该控件能够接收 value 和 onChange 两个基本属性即可。这两个属性是表单项数据和改变数据的事件处理函数,是实现表单控件与表单数据之间双向绑定的必要条件。因此,只要自定义组件能够接收这两个属性,就可以与 FormRender 进行良好的配合。

const CaptchaInput = (props: any) => { const { value, onChange, addons } = props; const sendCaptcha = (phone: string) => { console.log('send captcha to:', phone); } return ( <Space> <Input value={value} onChange={(e) => onChange(e.target.value)} placeholder="请输入手机号" /> <Button onClick={() => sendCaptcha(value)}>发送验证码</Button> </Space> ); };

import React from 'react'; import { Input, Button, Space } from 'antd'; import Form, { useForm } from 'form-render'; import CaptchaInput from './CaptchaInput'; const schema = { type: 'object', properties: { phone: { title: '自定义 Input', type: 'string', widget: 'CaptchaInput', props: {} } } }; const Demo = () => { const form = useForm(); return ( <Form form={form} schema={schema} widgets={{ CaptchaInput }} /> ); }; export default Demo; 嵌套组件自定义

在进行嵌套组件的自定义时,需要重点关注 children 属性。children 是嵌套组件的内部表单项,可以直接渲染而无需过多关注。FormRender 会将嵌套组件的 schema 字段透传到嵌套组件的 props 中,并将其打散以渲染对应的子表单项。因此,只要自定义组件能够正确接收 props 中的数据并渲染 children 中的表单项即可。

import React from 'react'; import { Card } from 'antd'; import './index.less'; const CustomCard = ({ children, title, description }) => { if (!title) { return children; } return ( <Card title={ <> {title} {description && ( <span className='fr-header-desc'> {description} </span> )} </> } > {children} </Card> ); } export default CustomCard;

import React from 'react'; import FormRender, { useForm } from 'form-render'; import CustomCard from './CustomCard'; const schema = { type: 'object', displayType: 'row', properties: { obj: { type: 'object', widget: 'CustomCard', title: '卡片主题', description: '这是一个对象类型', column: 3, properties: { input1: { title: '输入框 A', type: 'string' }, input2: { title: '输入框 B', type: 'string' }, input3: { title: '输入框 C', type: 'string' }, input4: { title: '输入框 D', type: 'string' } } } } }; export default () => { const form = useForm(); return <FormRender schema={schema} form={form} widgets={{ CustomCard }/>; }; 列表组件自定义

列表组件的实现稍微复杂一点,一般都会提供扩展参数来实现,但如果过于繁琐的话,可以参照源码对应的 list 组件进行自定义。

import React, { useState, useContext } from 'react'; import { Tabs } from 'antd'; import './index.less'; const TabPane = Tabs.TabPane; const TabList = (props) => { const { schema, fields, rootPath, renderCore, readOnly, delConfirmProps, tabName, hideDelete, hideAdd, addItem, removeItem, tabItemProps = {} } = props; return ( <Tabs> {fields.map(({ key, name }) => { return ( <TabPane> <div style={{ flex: 1 }}> {renderCore({ schema, parentPath: [name], rootPath: [...rootPath, name] })} </div> </TabPane> ); })} </Tabs> ); } export default TabList;

占位组件 (试验)

纯展示型组件,独占一行,可以用于表单模块分割使用,其他场景待开发

void2: { title: '其他组件', type: 'void', // 关键配置 widget: 'voidTitle' }

五、写在最后

作为一款开箱即用的表单解决方案,FormRender 可以大幅提高中后台系统中的表单开发效率和灵活性,让你可以快速创建各种类型的表单,并省略从头编写表单组件的繁琐步骤。我们将一直坚持这个初衷,并不断推进协议配置方面的创新和提升,努力提供更加完善的表单开发体验。

接下来我们将推出适配 2.0 协议的表单设计器,该设计器将支持自定义二次开发功能,并支持在移动端场景下拖拽生成 schema 协议。这将极大地提高表单设计和开发的便捷性。

最后感谢大家的支持与信任,我们欢迎在使用 FormRender 的过程中继续提出宝贵意见,帮助我们不断创造更具有价值的产品。

作者:斩鲌

来源:微信公众号:Fliggy FE

出处:https://mp.weixin.qq.com/s/Xg02sF9mbj-iG__wrWbPYg

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved