作為開發和前段人員,日常中經常接觸到有關表單。其實表單在日常中工作內容雖然是重復,也要不停在寫 FormItem...,以及為組件加上“請輸入/請選擇”等無腦的 placeholder 文本和“請輸入xx/請選擇xx”等必填提示。再有就是
表單一般都存在編輯頁和詳情頁,而為了代碼更好的維護性通常會將編輯和詳情用一套代碼實現。這樣就影響我們的代碼里就會出現“isEdit ? 表單組件 :簡單來說就是無大腦的簡單重復。現在就和大家說說如何優化升級這個無大腦。
實現
一般實現
// 一般實現 import React from 'react'; import { Form, Input, Select } from 'antd'; const Demo = (props) => { const { form: { getFieldDecorator }, obj = {}, isEdit } = props; return ( <> <FormItem label="姓名" > {isEdit ? obj.name || '-' :</p> <p> getFieldDecorator('name', {</p> <p> initialValue: obj.name,</p> <p> })( <Input placeholder="請輸入" /> ) } </FormItem> <FormItem label="性別" > {isEdit ? obj.sex || '-' :</p> <p> getFieldDecorator('sex', {</p> <p> initialValue: obj.sex,</p> <p> rules: [{ required: true, message: '請選擇性別' }], })( <Select placeholder="請選擇" > <Option key="male" value="male">男</Option> <Option key="female" value="female">女</Option> </Select> ) } </FormItem> <FormItem label="手機號" > {isEdit ? obj.phone || '-' :</p> <p> getFieldDecorator('phone', {</p> <p> initialValue: obj.phone,</p> <p> rules: [{ required: true, message: '請輸入手機號' }], })( <Input placeholder="請輸入" /> ) } </FormItem> <> ) }
配置化的實現
// 配置化的實現 import React from 'react'; import { renderDataForm } from 'src/util/renderDataForm'; const Demo = (props) => { const { form, obj = {}, isEdit } = props; const conf = [{ label: '姓名', // 表單的label field: 'name', // 表單字段名 initialValue: obj.name, // 表單默認值 required: false, // 是否必填、默認必填 }, { label: '性別', field: 'sex', initialValue: obj.sex, formItemType: 'Select', // 表單類型默認 Input options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }], // 下拉選項 }, { label: '手機號', field: 'phone', initialValue: obj.phone, }]; const dataForm = isEdit ? 'form' : 'text'; // 傳入form,表單配置,想要的數據形式 return renderDataForm(form, conf, dataForm)); }
實現思路
如上圖所示,表單組件的數據在詳情頁中顯示文本亦或是編輯頁中,其實顯而易見這兩個應用都是一樣,只是展現形式不同而已。在這里我們暫時將數據的形式定為表單組件形式與文本形式。其實在實際的使用中,由于數據的收集形式不同,會出現第三種數據形式。它就是表單文本形式,一種以文本展示但數據可被表單自動收集的形式,我把它定義為 FormText(如下所示)。好的,我們現在來梳理下,針對實現數據詳情與編輯形式的方案有了這樣兩種,表單與文本組合或表單與表單文本組合的實現。本次我選擇表單與文本組合的方案。
/** * 用于 Form 表單內部受控展示文本 */ export default class FormText extends Component { render() { const { value, formatMethod = a => a, defaultText = '-', ...resetProps } = this.props; return <span {...resetProps}>{formatMethod(value) || defaultText}</span>; } } // 使用 <FormItem label="姓名"> {getFieldDecorator('name', {</p> <p> initialValue: 'egg',</p> <p> })(<FormText />)} </FormItem>
具體實現
1、形式選擇(表單組件 or 文本)
const renderDataForm = (form, conf = {}, dataForm = 'form') => { // customRenderText 自定義文本形式 const { customRenderText } = conf; return ( <FormItem label={conf.label} {...conf.formItemProps} > {dataForm === 'form' ? renderFormItem(form, conf) :</p> <p> customRenderText ? customRenderText(conf) : renderText(conf) } </FormItem> ); };
2、表單組件選擇
export const renderFormItem = (form, rest) => { const { getFieldDecorator } = form; const { label = '', field = '', formItemType = 'input', initialValue, required = true, rules = [], ...itemRest } = rest; return (getFieldDecorator(field, { initialValue, rules: [ // 必填提示 { required, message: renderMessage(formItemType, label) }, ...rules, ], ...(formItemType === 'upload' ? { // Upload組件的通用配置 getValueFromEvent: (e) => { if (Array.isArray(e)) { return e; } return e && e.fileList; }, valuePropName: 'fileList' } : {}), })( renderItem(formItemType, itemRest) )); }; // 選擇表單組件 const renderItem = (formItemType, itemRest) => { const { options = [], CustomFormItem } = itemRest; const obj = { Input, TextArea, InputNumber, Upload, Select, RadioGroup, CheckboxGroup, DatePicker }; // 自定義的表單組件 if (formItemType === 'CustomFormItem') { return <CustomFormItem {...itemRest} />; } // 不存在對應組件時返回默認的 Input 組件 if (!obj[formItemType]) { return <Input placeholder="請輸入" {...itemRest} />; } const Comp = obj[formItemType]; // 雙標簽組件處理 if (['Select', 'Upload'].includes(formItemType)) { return formItemType === 'Upload' ? ( <Upload {...itemRest} > <Button><Icon type="upload" />上傳</Button> </Upload> ) : ( <Comp {...getDefaultCompProps(itemRest)} {...itemRest} > {options.map(el => (</p> <p> <Option key={el.value} value={el.value}>{el.label || el.name}</Option>))} </Comp> ); } // 單標簽組件 return <Comp {...getDefaultCompProps(itemRest)} {...itemRest} />; }; // 獲取組件屬性 const getDefaultCompProps = (conf = {}) => { const { formItemType } = conf; const props = {}; props.placeholder = renderMessage(formItemType); if (formItemType === 'InputNumber') { // zeroOmit 小數點后多余的零是否省略,limitDecimal 限制最長的小數位數 const { zeroOmit = true, limitDecimal = 6 } = conf; const limitDecimalsF = (value) => { const reg = new RegExp(`^(-)*(\\d+)\\.(\\d{${limitDecimal}}).*$`); return `${value}`.replace(reg, '$1$2.$3'); }; if (zeroOmit) { props.formatter = limitDecimalsF; props.parse = limitDecimalsF; } } if (formItemType === 'Input') { props.maxLength = 100; // 輸入框的默認最大輸入字符長度 } if (formItemType === 'TextArea') { props.maxLength = 500; // 文本框的默認最大輸入字符長度 } return props; };
3、映射文本
export const renderText = (rest) => { const { formItemType = 'Input', initialValue, selectOptions = [], selectMode = '', options = [] } = rest; switch (formItemType) { case 'RadioGroup': return (options.find(item => item.value === initialValue) || {}).label || '-'; case 'DatePick': const { format = 'YYYY-MM-DD HH:mm:ss' } = rest; // 日期組件組件值格式化為對應的 文本 return initialValue !== undefined ? moment(initialValue).format(format) : '-'; // ...code default: return bizStringFormat(initialValue); // 無 值 時 默認 ‘-' } }
4、通用校驗規則整理
export const postCode = /^[0-9]{6}$/; export const phone = /^1\d{10}$/; // ...其他正則 // form rules export const postCodeRule = { pattern: postCode, message: '請輸入6位數字', }; export const phoneRule = { pattern: phone, message: '請輸入11位號碼', }; // ...其他表單校驗規則
使用示例
const Demo = (props) => { const { form } = props; // 數據 const obj = { email: '123@egg.com', addr: '派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星派大星', sort: 'up', birthday: '1999-01-23', sex: 'male', file: [{ fileId: '123', name: '信用承諾書', size: 1024 }], }; // 因為數據的形式默認為表單,所以 dataForm: 'form' 可不配置 const formConf = [{ label: '郵箱', field: 'email', initialValue: obj.email, rules: [emailRule], // emailRule 為郵箱校驗規則 }, { label: '地址', field: 'addr', initialValue: obj.addr, formItemType: 'TextArea', }, { label: '排序', field: 'sort', initialValue: obj.sort, formItemType: 'Select', options: [{ value: 'up', label: '升序' }, { value: 'down', label: '降序' }], }, { label: '生日', field: 'birthday', initialValue: obj.birthday, formItemType: 'DatePicker', format: 'YYYY-MM-DD', // 日期組件的格式配置字段 }, { label: '性別', field: 'sex', initialValue: obj.sex, formItemType: 'RadioGroup', options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }], }, { label: '信用承諾書', field: 'file', initialValue: obj.file, formItemType: 'Upload', }]; const dataForm = isEdit ? 'form' : 'text'; // 將配置遍歷傳入renderDataForm // 當然你也可以封裝成組建,直接向組建傳入 form、formConf,減少遍歷的重復書寫和整潔 return formConf.map(item => renderDataForm(form, item, dataForm));
最終呈現如下:
1.編輯
2. 觸發校驗
3.詳情
已全部講述完畢,即使在目前的前端領域,關于頁面配置、可視化等更加復雜的能力,在各個方面都全面更新和展現。但優化還是要不斷繼續。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/128139.html
摘要:而中實現原理是利用高階函數通過將多個函數組合成一個可執行執行函數關鍵步驟代碼如下所示。和都是基于更新差異元素。 引言 平時開發單頁項目應用基于vue,目前另外兩個比較熱的庫還有angular和react,angular 1系列用過,進入公司后由于基于vue技術棧就沒在關注了。一直在關注react,目的不是學習用法,只是為了拓展自己的視野和思維,通過了解一些使用上的差異性,來進一步的思考...
摘要:是前端開發領域新興的方法論體系,它繼承了與編程理念,在技術上有不少創新。但專利與開源協議是平行的兩個世界,改底層也不大容易解決問題。此外,要求在中結合各屬性的是否變化,判斷是否該觸發更新。 ReRest (Reactive Resource State Transfer) 是前端開發領域新興的方法論體系,它繼承了 MVVM 與 FRP 編程理念,在技術上有不少創新。本文從專利稿修改而來...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28