antd form表單如何處理自定義組件
antd form表單處理自定義組件
使用 antd 中的 Form 組件時(shí),若使用自定義組件時(shí),保存的時(shí)候獲取不到自定義組件中的值。
以下是從antd找到的解決思路
被設(shè)置了 name 屬性的 Form.Item 包裝的控件,表單控件會(huì)自動(dòng)添加 value(或 valuePropName 指定的其他屬性) onChange(或 trigger 指定的其他屬性)
我們可以通過(guò)表單控件自動(dòng)添加的onChage,來(lái)做數(shù)據(jù)收集同步。
父組件:
import React, { Fragment, useState, useEffect, forwardRef, useRef } from 'react'; import HeaderTable from './headerTable'; const BasicForm = forwardRef((props, ref) => { ? const { record } = props; ? const [ form ] = Form.useForm(); ? ? <Form ? ? ? ? ? name="taskForm" ? ? ? ? ? ref={ref} ? ? ? ? ? form={form} ? ? ? ? > ? ? ? ? <Row gutter={20}> ? ? ? ? ? ? <Col span={24}> ? ? ? ? ? ? ? <Form.Item ?name="headers" label={'Header'}> ? ? ? ? ? ? ? ? <HeaderTable ref={headerTableRef} record={record} /> ? ? ? ? ? ? ? </Form.Item> ? ? ? ? ? ? </Col> ? ? ? ? ? </Row> ? ? </Form> export default BasicForm;
子組件:
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef} from 'react'; import { Transfer, Divider, Table, Button, Input, message, Select } from 'antd'; import AddRowTable from '@components/AddRowTable';//Table組件 const { Option } = Select; const DATATYPE = [ ?? ?{ ?? ??? ?id: 1, ?? ??? ?name: 'int' ?? ?}, { ?? ??? ?id: 2, ?? ??? ?name: 'varchar' ?? ?} ] const HeaderTable = ( props, ref ) => { ?? ?const { record, isLook, onChange } = props; ?? ?const addRowTable = useRef(); ?? ?const [ sourceData, setSourceData ] = useState([]); ?? ?const [ columns, setColumns ] = useState([]); ?? ?const [ targetKeys, setTargetKeys ] = useState([]); ?? ?const [ headerList, setHeaderList ] = useState([]); ?? ?useEffect(() => { ?? ??? ?if (record && record.id) {//當(dāng)編輯時(shí)將傳來(lái)的數(shù)據(jù)賦值 ?? ??? ??? ?setHeaderList(record.sourceInfoMap.headers) ?? ??? ?} ?? ?}, [record]) ? ? ? ?? ? ? ? ? //設(shè)置columns表頭 ? ? ? ? useEffect(() => { ? ? ? ? const columns = [ ?? ?{ ? ? ? ? ? ? title: '參數(shù)名稱', ? ? ? ? ? ? dataIndex: 'name', ? ? ? ? ? ? render: (text, record) => { ?? ??? ?return ( ?? ??? ??? ?<Input value={text} onChange={tableItemChange.bind(this, record, 'name')} /> ?? ??? ??? ?); ?? ??? ?} ?? ?}, ? ? ? ? { ? ? ? ? ? ? title: '操作', ? ? ? ? ? ? dataIndex: 'operation', ? ? ? ? ? ? align: 'center', ? ? ? ? ? ? render: (text, record, index) => { ?? ??? ?return ( ? ? ? ? ? ? ? ? ? ? <a onClick={delHandle.bind(this, record, index)}>'Delete'</a> ? ? ? ? ? ? ? ? ? ? ? ? ); ?? ??? ?} ?? ?}]; ? ? ? ?? ? ? ? ? setColumns(columns); ? ? ? ?? ? ? ? ? },[]) ?? ?// 添加一行 ?? ?const handleAddTableRow = () => { ?? ??? ?setHeaderList(headerList => [...headerList, {}]); ?? ??? ?onChange(headerList => [...headerList, {}]) ?? ?}; ?? ?// 刪除一行 ?? ?const delHandle = (record, index) => { ?? ??? ?setHeaderList(headerList => { ?? ??? ??? ?headerList.splice(index, 1); ?? ??? ??? ?onChange(headerList) ?? ??? ??? ?return [...headerList]; ?? ??? ?}); ?? ?}; ?? ?// 表格中數(shù)據(jù)更改 ?? ?const tableItemChange = (data, keyField, evt) => { ?? ??? ?const value = evt.target ? evt.target.value : evt; ?? ??? ?data[keyField] = value; ?? ??? ?onChange(headerList) ?? ?}; ?? ?return <div> ?? ??? ?<AddRowTable ?? ??? ??? ?ref={addRowTable} ?? ??? ??? ?dataSource={headerList} ?? ??? ??? ?columns={columns} ?? ??? ??? ?isCanAdd={isLook} ? ? ? disabled={isLook} ?? ??? ??? ?addItem={handleAddTableRow.bind(this)} ?? ??? ?/> ?? ?</div> } export default HeaderTable;
以上操作就可以在表單中使用自定義組件,表單也能統(tǒng)一獲取值。
不僅僅input輸入框可以這么操作,所有組件,只要你拿到值后,把值給 onChange 方法,都是可以統(tǒng)一獲取值的。
antd form表單中嵌套自定義組件
當(dāng)某些自定義的組合類組件,也希望能進(jìn)行表單元素的校驗(yàn),以及利用antd的form表單實(shí)例進(jìn)行數(shù)據(jù)的修改或者數(shù)據(jù)獲取,這招便可以派上用場(chǎng)啦~
表單部分
<Form.Item label="周期" labelAlign="right"> {getFieldDecorator('cycle', { rules: [{ required: true, validator: checkCycle }], initialValue: cycle, })(<CycleInput cycleOptions={cycleOptions} />)} </Form.Item>
表單部分可以引入自定義的校驗(yàn)邏輯,如上面的checkCycle :
可以像下面這樣,通過(guò)Promise.reject()返回錯(cuò)誤校驗(yàn)提示,通過(guò)Promise.resolve()正確通過(guò)校驗(yàn)。
/** 校驗(yàn)生命周期輸入內(nèi)容 */ const checkCycle = useCallback((_, value) => { const format = value?.split(' '); if (!format) return Promise.reject(new Error('周期不可為空!')); /** always 永久保存選項(xiàng) */ if (format.length < 2 && format[0] === 'always') { return Promise.resolve(); } if (format[1] === 'undefined') { return Promise.reject(new Error('請(qǐng)選擇周期!')); } const num = +format[0]; if (num > 0 && format[1]) { return Promise.resolve(); } return Promise.reject(new Error('請(qǐng)輸入大于 0 的數(shù)字!')); }, []);
有關(guān)校驗(yàn)的內(nèi)容,antd也是參考一個(gè)成熟的庫(kù) async-validator,上面這種寫法可以參考文檔這塊 asyncValidator
如果懶得看文檔直接看圖:
大概意思就是說(shuō),可以通過(guò)兩種方式來(lái)實(shí)現(xiàn)一些異步校驗(yàn):
(1)可以通過(guò)調(diào)用callback,即asyncValidator的第三個(gè)參數(shù),來(lái)完成校驗(yàn)
(2)可以通過(guò)返回Promise,來(lái)完成校驗(yàn)
自定義組件
注意value是該表單域的值,onChange是對(duì)應(yīng)可以改變?cè)摫韱斡虻闹档姆椒?/p>
const CycleInput = ({ value, onChange, cycleOptions }) => { const format = value?.split?.(' '); const num = format?.[0]; const type = format?.[1]; const triggerChange = changedValue => { onChange && onChange(changedValue); }; /** 根據(jù)后端接口返回的數(shù)據(jù),type 有值,則為 day、week、month、year 其一 */ /** type 若為 undefined,則 num 為 always */ const [cycleOption, setCycleOption] = useState(type ?? num); const [cycleInputValue, setCycleInputValue] = useState( (typeof +num === 'number' && !_.isNaN(+num) && num) || '', ); const onCycleChange = cycleOption => { setCycleOption(cycleOption); triggerChange( cycleOption === 'always' ? cycleOption : `${cycleInputValue} ${cycleOption}`, ); }; const onInputChange = e => { const newNumber = parseInt(e.target.value || '0', 10); if (_.isNaN(newNumber)) { return; } setCycleInputValue(newNumber); triggerChange( cycleOption === 'always' ? cycleOption : `${newNumber} ${cycleOption}`, ); }; return ( <Input.Group> {cycleOption === 'always' ? ( <Select onChange={onCycleChange } defaultValue={cycleOption} style={{ width: 240 }} > {cycleOptions.map(item => ( <Select.Option key={item.value} value={item.value}> {item.label} </Select.Option> ))} </Select> ) : ( <span> <Input value={cycleInputValue} style={{ width: 156 }} onChange={onInputChange} /> <Select onChange={onCycleChange} defaultValue={cycleOption} style={{ width: 84 }} > {cycleOptions.map(item => ( <Select.Option key={item.value} value={item.value}> {item.label} </Select.Option> ))} </Select> </span> )} </Input.Group> ); };
可以看出,自定義部分邏輯也比較簡(jiǎn)單,通過(guò)value參數(shù)可以展示組件的值,通過(guò)onChange方法,可以改變表單域的值,實(shí)現(xiàn)了表單帶給我們的便捷功能。
總結(jié)
到此結(jié)束啦,快試試吧,實(shí)踐出真知呀!
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
react使用websocket實(shí)時(shí)通信方式
這篇文章主要介紹了react使用websocket實(shí)時(shí)通信方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09React使用fullpage.js實(shí)現(xiàn)整屏翻頁(yè)功能
最近筆者在完成一個(gè)移動(dòng)端小項(xiàng)目的過(guò)程中需要實(shí)現(xiàn)整屏翻頁(yè)的效果;調(diào)研完畢之后,最終決定使用pullPage.js實(shí)現(xiàn)此功能,fullPage.js使用起來(lái)比較方便,并且官網(wǎng)上也給了在react項(xiàng)目中使用的demo,本文記錄了這個(gè)第三方庫(kù)的使用過(guò)程,感興趣的朋友可以參考下2023-11-11詳解React Native 屏幕適配(炒雞簡(jiǎn)單的方法)
React Native 可以開發(fā) ios 和 android 的 app,在開發(fā)過(guò)程中,勢(shì)必會(huì)遇上屏幕適配,這篇文章主要介紹了詳解React Native 屏幕適配(炒雞簡(jiǎn)單的方法),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06React內(nèi)部實(shí)現(xiàn)cache方法示例詳解
這篇文章主要為大家介紹了React內(nèi)部實(shí)現(xiàn)cache方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11webpack+react+antd腳手架優(yōu)化的方法
本篇文章主要介紹了webpack+react+antd腳手架優(yōu)化的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04React通過(guò)redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例
這篇文章主要介紹了React通過(guò)redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02React Router 如何使用history跳轉(zhuǎn)的實(shí)現(xiàn)
這篇文章主要介紹了React Router 如何使用history跳轉(zhuǎn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04React 如何使用時(shí)間戳計(jì)算得到開始和結(jié)束時(shí)間戳
這篇文章主要介紹了React 如何拿時(shí)間戳計(jì)算得到開始和結(jié)束時(shí)間戳,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09