JSON生成Form表單的方法示例
JSON表單描述
JSON表單是一個(gè)基于React的抽象組件,它可以把JSON數(shù)據(jù)格式描述的表單轉(zhuǎn)換成項(xiàng)目中的表單,它可以用簡(jiǎn)短的幾行代碼,快速的生成Form表單。
JSON表單的優(yōu)點(diǎn)是:
- 可以快速構(gòu)建出一個(gè)表單
- 表單的數(shù)據(jù)、邏輯、視圖分離,方便抽離和進(jìn)一步抽象
- 提供校驗(yàn)、自動(dòng)緩存等額外功能,提升錄入體驗(yàn)
- 可以跨項(xiàng)目的共用復(fù)雜的表單組件
原始表單的缺點(diǎn)
1:代碼量龐大,開(kāi)發(fā)效率低
每次開(kāi)發(fā)一個(gè)表單頁(yè)的時(shí)候,都需要重復(fù)編寫(xiě)表單組件及其交互事件的代碼,這塊代碼重復(fù)編寫(xiě)且與主線業(yè)務(wù)邏輯無(wú)關(guān),除此之外,表單的校驗(yàn)、緩存等額外功能,也需要不少的代碼量,這樣就造成了一個(gè)表單頁(yè)的代碼量龐大。
2:不便于抽離和進(jìn)一步的抽象
在一個(gè)表單頁(yè)內(nèi),往往會(huì)將表單數(shù)據(jù)、表單組件、控制邏輯雜糅在一起,當(dāng)發(fā)現(xiàn)某一個(gè)子功能在很多場(chǎng)景下都需要用到的時(shí)候,想把該子功能拆分出來(lái)發(fā)現(xiàn)并不容易,因?yàn)檫壿?、?shù)據(jù)、視圖的雜糅,導(dǎo)致想把子功能不受影響的剔除出來(lái)需要很仔細(xì)的檢查,這樣就導(dǎo)致功能的抽離和抽象的不便
3:維護(hù)成本高
這個(gè)和第二個(gè)問(wèn)題是同樣的原因,這也是我的親身經(jīng)歷,當(dāng)我在項(xiàng)目中想優(yōu)化一個(gè)小功能的時(shí)候,發(fā)現(xiàn)不僅把之前的邏輯改沒(méi)了,還引出了不少的bug,這導(dǎo)致在一個(gè)邏輯很復(fù)雜的表單里,維護(hù)變成了一件高危工作。
4:需要額外處理校驗(yàn)和緩存等功能
一個(gè)栗子
const config = {
formKey: 'example-form',
data: {
name: '',
descr: '',
typeName: ''
},
config: [
{
type: 'input',
dataKey: 'name',
label: 'param',
placeholder: '請(qǐng)輸入param',
validate: ['required', /^[a-zA-Z_{}0-9]+$/g],
style: {
display: 'inline-block',
width: 270,
},
},
{
type: 'select',
dataKey: 'typeName',
options: ['string', 'integer', 'float'],
style: {
display: 'inline-block',
width: 100,
margin: '0 15px'
},
validate: [{type: 'required', message: 'param類(lèi)型不能為空'}]
},
{
type: 'textarea',
dataKey: 'descr',
placeholder: '請(qǐng)輸入param含義',
label: 'param含義',
validate: ['required'],
style: {
width: 385,
}
},
]
}
<From ref={ref => this.FormWrap = ref} config={config}></From>
上面是用JSON描述的三個(gè)常用的表單組件組合成的表單,其效果圖如下:

JSON表單的格式
{
formKey: 'paramAddForm',
data: {},
config: []
}
| 屬性 | 是否必傳 | 說(shuō)明 | 類(lèi)型 | 默認(rèn)值 |
|---|---|---|---|---|
| formKey | 否 | 用來(lái)自動(dòng)緩存,localStorage的key,不傳表示不自動(dòng)緩存 | string | - |
| className | 否 | 用來(lái)添加一些自定義樣式 | string | - |
| data | 是 | 表單的提交數(shù)據(jù),有自動(dòng)緩存和校驗(yàn)功能 | object | - |
| assisData | 否 | 用于表單控制邏輯的額外數(shù)據(jù) | object | - |
| config | 是 | 組件配置,表單組件的配置 | Array | - |
| realTimeSubmit | 否 | 表單是否實(shí)時(shí)提交,一般用于篩選表單 | boolean | false |
表單組件的配置
{
type: 'input',
dataKey: 'name',
label: 'param',
validate: ['required'],
style: {}
}
| 屬性 | 是否必傳 | 說(shuō)明 | 類(lèi)型 | 默認(rèn)值 |
|---|---|---|---|---|
| type | 是 | 表單組件的類(lèi)型,其值可以為: input、select、textarea、form_array、container和一些自定義表單組件 | string | - |
| dataKey | 否 | 指定表單組件值的key,可以為param.name.firstName形式 | string | - |
| label | 否 | 表單組件的label | string | - |
| placeholder | 否 | 表單組件的placeholder | string | - |
| validate | 否 | 表單組件的校驗(yàn)規(guī)則 | Array | - |
| style | 否 | 表單組件的布局樣式 | string | - |
| options | 否 | 當(dāng)表單組件為select時(shí),需要傳入的options | Array | - |
| render | 否 | 當(dāng)type為container時(shí),為自定義組件,render為渲染方法 | Function | - |
| preventSubmit | 否 | 當(dāng)realTimeSubmit為true時(shí),控制當(dāng)前表單組件是否實(shí)時(shí)提交 | boolean | false |
| children | 否 | 當(dāng)type為form_array時(shí),children表示子組件配置列表 | Array | - |
| modifyDataFn | 否 | 當(dāng)type為自定義組件時(shí),且需要覆蓋render方法中的提交數(shù)據(jù)方法,可以使用modifyDataFn來(lái)重新自定義提交數(shù)據(jù) | Function | - |
關(guān)鍵字段解釋
1. type
type是用來(lái)唯一表示表單組件類(lèi)型的字段,其中JSON表單提供了三種默認(rèn)的表單組件:input、select、textarea,還有兩種復(fù)雜類(lèi)型的表單組件:form_array、container。
form_array表單組件表示其數(shù)據(jù)結(jié)構(gòu)為Array,含有增加項(xiàng)刪除項(xiàng)的復(fù)合表單組件,該表單組件的配置里多一個(gè)children的字段,里面是每一項(xiàng)里面的表單組件配置的集合,其表單組件的效果如下圖所示:

container是用來(lái)自定義表單的接口,具體用法參考下面具體的介紹。
2. validate
validate是校驗(yàn)表單組件數(shù)據(jù)正確性的字段,其值為數(shù)組,里面的數(shù)組元素可以為String、object、RegExp、Function。
JSON表單采用的是async-validator異步處理校驗(yàn),在JSON表單內(nèi)部會(huì)將validate傳入的校驗(yàn)關(guān)鍵字解析為async-validator的rules。所以validate數(shù)組元素如果為object的話,其內(nèi)容就是async-validator的rules。
1. 數(shù)組元素為string,其值可以為:
- string,值必須為string
- number,值必須為數(shù)字
- required,值不能為空
- boolean,值必須為布爾值
- integer,值必須為整數(shù)形
- float,值必須為浮點(diǎn)型
- email,值必須為郵箱類(lèi)型
2. 數(shù)組元素為object,其值為rules:
{type: 'enum', enum: ['1', '2'], message: '值不在確定范圍內(nèi)'}
3. 數(shù)組元素為RegExp, validate: [/^[a-zA-Z_{}0-9]+$/g]
4. 數(shù)組元素為Function, validate: [ (rules, value, callback) => {}]
3. style
用來(lái)確定表單組件在表單內(nèi)的布局樣式,比如想讓表單組件行內(nèi)顯示,且寬度為200,其style值如下:
{
display: 'inline-block',
width: 200
}
container表單組件
container表單組件是用來(lái)自定義表單組件的,它主要的作用有以下幾點(diǎn):
- 添加表單組件庫(kù),例如Ant-Design
- 自定義表單組件,例如圖片上傳組件
- 用來(lái)構(gòu)建復(fù)雜的表單組件
- 處理控制邏輯和聯(lián)動(dòng)邏輯
使用栗子
import { Input, Select } from 'antd'
const Option = Select.Option
{
type: 'container',
dataKey: 'descr',
style: {
display: 'inline-block',
width: 100,
margin: '0 15px'
},
options: ['string', 'integer', 'float'],
render: (curData, config, {changeFn, getFocus, loseFocus, error}) => {
return <Select value={curData}
style={{width: '100%', height: 35}}
onMouseEnter={getFocus}
onChange={(value) => changeFn(value, () => {
loseFocus()
})}>
{
config.options && config.options.map((item, idx) => <Option key={idx} value={item}>{item}</Option>)
}
</Select>
}
},
container表單組件只是多一個(gè)render渲染方法,里面可以自定義表單組件的渲染內(nèi)容,render方法提供如下參數(shù):
1. curData: 當(dāng)前container組件的值,跟dataKey相關(guān)
2. config: 當(dāng)前container組件的配置
3:{changeFn, changeDataFn, getFocus, loseFocus, error, JSONForm}
changeFn, changDataFn是提交數(shù)據(jù)的方法,changeFn只能修改當(dāng)前組件dataKey的值,changeDataFn可以修改data中任意字段的值,changeFn(value, [callback]), changeFn(dataKey, value, [callback])
getFocus,loseFocus是自定義處理校驗(yàn)的字段,loseFocus是開(kāi)始校驗(yàn),getFocus是去掉校驗(yàn)的報(bào)錯(cuò)信息
error是校驗(yàn)結(jié)果的報(bào)錯(cuò)信息
JSONForm是在container中使用JSON表單的組件配置用來(lái)生成新的表單組件,意思里container中依然可以嵌套表單組件。
使用antd的組件庫(kù)
JOSN表單只提供了input、select、textarea三種默認(rèn)的表單組件,遠(yuǎn)遠(yuǎn)不夠真實(shí)的項(xiàng)目中使用,所以我們可以將antd組件庫(kù)中的組件封裝到JSON表單中,這樣我們就可以再項(xiàng)目中很快的使用antd中的組件。
antd-components.js
import React from 'react'
import { Input } from 'antd'
export default [
{
type: 'antd-input',
render: (curData, config, {changeFn, getFocus, loseFocus, error}) => {
return <Input value={curData}
onFocus={getFocus}
onBlur={loseFocus}
placeholder={config.placeholder ? config.placeholder : ''}
style={{borderColor: !!error ? '#f5222d' : ''}}
onChange={event => changeFn(event.target.value)} />
}
}
]
我們?cè)赼ntd-components.js文件中聲明一個(gè)antd-input的自定義組件,然后在JSON表單中引入該自定義表單組件:
init.js
import Form from 'Form'
import components from './antd-components'
From.createCustomComp(components)
const config = {
formKey: 'paramAddFromAntd',
data: {
name: '',
},
config: [
{
type: 'antd-input',
dataKey: 'name',
label: 'Param',
placeholder: '請(qǐng)輸入param',
validate: ['required', /^[a-zA-Z_{}0-9]+$/g]
}
]
}
<From ref={ref => this.FormWrap = ref} config={config}></From>
使用container來(lái)引入antd組件庫(kù),其原理就是通過(guò)container將antd組件封裝成'antd-input'自定義組件,然后使用它,這種方式不僅可以用來(lái)封裝組件庫(kù),還可以用來(lái)共享一些共用表單組件,可以將常用的復(fù)雜表單組件封裝在一個(gè)共用文件里,然后在不同項(xiàng)目中引用,就可以跨項(xiàng)目共用表單組件。
在自定義組件中,如果需要自定義表單提交數(shù)據(jù)函數(shù),但是又不能重寫(xiě)render方法以防覆蓋原先的render方法,所以可以使用modifyDataFn方法來(lái)覆蓋render中的提交數(shù)據(jù)部分。
modifyDataFn: ({changeFn, changeDataFn}, {parent, self}) => {
let {parentData} = parent
parentData = parentData.map(item => ({
...item,
name: self.curData
}))
changeDataFn(parent.parentKey, parentData)
}
處理控制邏輯和聯(lián)動(dòng)邏輯
在JSON表單JSON配置中,有assistData的選填字段,該字段為JSON表單處理控制邏輯的額外數(shù)據(jù),例如在表單內(nèi)有一個(gè)刷新按鈕,其實(shí)現(xiàn)代碼如下:
{
data: {},
assistData: {
refreshParam: false
},
config: [
{
type: 'container',
dataKey: 'assistData.refreshParam',
render: (curData, config, {changeFn, changeDataFn}) => {
const handleClick = () => {
changeDataFn('assistData.refreshParam' ,true)
setTimeout(() => {
changeDataFn('assistData.refreshParam' ,false)
}, 1000 * 3)
}
return <React.Fragment>
{
config.index === config.parentData.length - 1 &&
<Popover placement="top" content="刷新param列表">
<Button shape="circle" loading={curData} onClick={handleClick}>{!curData && <Icon type="reload" />}</Button>
</Popover>
}
</React.Fragment>
}
},
]
}
注意: 如果要使用assistData中的數(shù)據(jù),其dataKey必須以assistData開(kāi)頭,且必須使用changeDataFn自定義提交assistData數(shù)據(jù)。
render方法內(nèi)嵌套組件配置
{
type: 'container',
dataKey: 'param',
render: (curData, config, {changeFn, changeDataFn, JSONForm}) => {
return <div>
{
JSONForm([
{
type: 'input',
dataKey: 'name',
placeholder: '請(qǐng)輸入param',
validate: ['required'],
}
])
}
</div>
}
這樣就可以在container內(nèi)嵌套組件配置,實(shí)現(xiàn)更復(fù)雜的表單組件。
JSON表單提交數(shù)據(jù)
非實(shí)時(shí)表單提交
非實(shí)時(shí)表單提交數(shù)據(jù),就是在表單輸入完畢后,點(diǎn)擊提交按鈕統(tǒng)一提交所有的數(shù)據(jù),其提交的方式如下:
function handleClick() {
this.FormRefs.getValue((valid, data) => {
// valid 表示校驗(yàn)結(jié)果,false表示校驗(yàn)不通過(guò)
})
}
實(shí)時(shí)表單提交
實(shí)時(shí)表單的提交首先需要注冊(cè)提交函數(shù):
componentDidMount(){
this.FormRefs.registerSubmit((valid, data) => {
console.log(valid, data)
})
}
接著在配置里設(shè)置允許實(shí)時(shí)提交的字段:
{
formKey: '',
realTimeSubmit: true
}
如果需要在某些表單組件里自定義是否實(shí)時(shí)提交,需要在組件配置里設(shè)置阻止實(shí)時(shí)提交字段為true:
{
dataKey: '',
preventSubmit: true
}
JSON表單的應(yīng)用場(chǎng)景
表單分類(lèi)
a. 按復(fù)雜度分類(lèi)
1. 簡(jiǎn)單表單:表單組件為input、select、textarea等常見(jiàn)的幾種,且表單組件之間邏輯獨(dú)立
2. 復(fù)雜表單:表單組件內(nèi)容和交互復(fù)雜且相互之間存在復(fù)雜的邏輯
其中復(fù)雜表單又可以分為:
1. 聯(lián)動(dòng)表單,上一個(gè)表單組件會(huì)影響接下來(lái)表單的值
2. 實(shí)時(shí)表單,表單組件的事件會(huì)觸發(fā)表單的實(shí)時(shí)提交,例如篩選表單
3. 富控制表單,表單內(nèi)部含有很多的控制邏輯
JSON表單最適合的應(yīng)用場(chǎng)景是簡(jiǎn)單表單,它可以用極少的代碼,快速的構(gòu)建出表單來(lái),對(duì)于復(fù)雜類(lèi)型的表單,JSON表單需要使用container來(lái)構(gòu)建復(fù)雜的表單組件、處理復(fù)雜的控制邏輯,其代碼量?jī)?yōu)勢(shì)雖然并不明顯,但是JSON表單可以使其代碼清晰,將表單組件和表單邏輯徹底解耦,便于抽離和維護(hù),便于共享常用組件,也帶來(lái)不少的好處。
到目前為止,JSON表單適合大部分的表單應(yīng)用場(chǎng)景。
JSON表單解決的問(wèn)題
- 減少了表單代碼量,不需要重復(fù)的開(kāi)發(fā)表單組件,只需要輸入組件配置即可
- 將表單組件和數(shù)據(jù)解耦,便于子功能的拆分和常用組件的共享
- 簡(jiǎn)化了校驗(yàn)功能,只需要傳入validate字段即可
- 添加了自動(dòng)緩存功能
在我的項(xiàng)目,我嘗試了使用原始表單和JSON表單兩種方式來(lái)實(shí)現(xiàn)同一個(gè)表單頁(yè),原始表單我編寫(xiě)了600多行的代碼,而在JSON表單中,只有不到150行。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 跨域解決之JSONP和CORS的詳細(xì)介紹
- $.ajax中contentType: “application/json” 的用法詳解
- 簡(jiǎn)單說(shuō)說(shuō)angular.json文件的使用
- JavaScript根據(jù)json生成html表格的示例代碼
- MongoDB使用mongoexport和mongoimport命令,批量導(dǎo)出和導(dǎo)入JSON數(shù)據(jù)到同一張表的實(shí)例
- nodejs讀取本地中文json文件出現(xiàn)亂碼解決方法
- Go語(yǔ)言的JSON處理詳解
- JavaScript JSON使用原理及注意事項(xiàng)
相關(guān)文章
一文帶你了解小程序中的權(quán)限設(shè)計(jì)
我們?cè)谌粘I钪袩o(wú)論是坐公交還是點(diǎn)餐,都會(huì)接觸各種各樣的小程序,下面這篇文章主要給大家介紹了關(guān)于小程序中權(quán)限設(shè)計(jì)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
javascript實(shí)現(xiàn)根據(jù)3原色制作顏色選擇器的方法
這篇文章主要介紹了javascript實(shí)現(xiàn)根據(jù)3原色制作顏色選擇器的方法,涉及javascript操作頁(yè)面元素屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
js 操作table之 移動(dòng)TR位置 兼容FF 跟 IE
js操作table之 移動(dòng)TR位置 兼容FF 跟 IE,需要的朋友可以參考下。2009-11-11
詳解如何在JavaScript中創(chuàng)建線性儀表圖
線性儀表圖表示顯示所需值的垂直或水平線性刻度,帶有顏色刻度以及單個(gè)或多個(gè)指針。本文將詳細(xì)講解如何利用JavaScript創(chuàng)建線性儀表圖,需要的可以參考一下2022-03-03
JS碰撞運(yùn)動(dòng)實(shí)現(xiàn)方法詳解
這篇文章主要介紹了JS碰撞運(yùn)動(dòng)實(shí)現(xiàn)方法,詳細(xì)分析了碰撞運(yùn)動(dòng)的原理及相應(yīng)的javascript實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-12-12
js實(shí)現(xiàn)千分符和保留幾位小數(shù)的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇js實(shí)現(xiàn)千分符和保留幾位小數(shù)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08

