我需要构建一个表单生成器组件,该组件基于json格式的后端数据构建表单。Json在描述后给出如下。Form组件可以有任意数量和类型的字段。作为第一步我缩小了可能遇到的字段类型的数量。因此,我认为在我的例子中,我将处理7种类型的输入->
- 选择 文件
- text类型输入
- 数字类型输入
. .现在我可以根据我从json接收的任何fieldType属性的值创建表单元素…如果只有一个特定字段类型的字段,那么一切都很好。因为我可以为每个formField类型声明7个状态变量来存储它们的值并执行表单验证。
const [textInputData, setTextInputData] = useState("")
const [numberInputData, setNumberInputData] = useState("")
const [emailInputData, setEmailInputData] = useState("")
const [passwordInputData, setPasswordInputData] = useState("")
const [dateInputData, setDateInputData] = useState("")
const [fileInputData, setFileInputData] = useState("")
const [selectInputData, setSelectInputData] = useState("")
但是问题是在我的表单中可以有任何数量的特定字段类型-例如:在我的表单中可以有多个电子邮件类型字段,例如一个用于客户电子邮件id,另一个用于供应商电子邮件id。
问题是->是否有任何方式(或它是可能的),我可以知道有多少表单字段将来自后端,并基于表单字段及其标签的数量(见json)创建状态…或者我应该使用不同的方法…
这是我正在处理的组件->
import {useState } from "react"
import {useDispatch, useSelector} from 'react-redux'
import {TextField, Autocomplete, Box, InputAdornment, FormControl, MenuItem, Button} from '@mui/material'
import DatePicker from "components/common/controls/DatePicker"
import { CloudUpload } from "@material-ui/icons"
import { makeStyles } from "@mui/styles"
import Select from "components/common/controls/Select"
import { useForm, Controller } from "react-hook-form"
import {yupResolver} from '@hookform/resolvers/yup'
import * as yup from 'yup'
const useStyles = makeStyles({
textField: {
'& .MuiInputBase-input': {
opacity: '0'
}
}
})
const formData = []
// json data provided below
export const FormGenerator = () => {
const leadData = useSelector(state => state.lead)
const [textInputData, setTextInputData] = useState("")
const [numberInputData, setNumberInputData] = useState("")
const [emailInputData, setEmailInputData] = useState("")
const [passwordInputData, setPasswordInputData] = useState("")
const [dateInputData, setDateInputData] = useState("")
const [fileInputData, setFileInputData] = useState("")
const [selectInputData, setSelectInputData] = useState("")
const dispatch = useDispatch()
const classes = useStyles()
const schema = yup.object().shape({
email: yup.string().required('Email is required').email('Email is Invalid'),
password: yup.string().min(6).max(20).required(),
textInput: yup.string().required(),
numberInput: yup.number().required().positive().integer(),
date: yup.date().required()
})
const {register, handleSubmit, control, formState: {errors}} = useForm({
resolver: yupResolver(schema)
})
// check fieldType and render component accordingly
const createFormElement = (el, index) => {
console.log(el)
switch(el.fieldType) {
case "textInput":
return createInputElement(el, index, 'textInput')
case "numberInput":
return createInputElement(el, index, 'numberInput')
case "email":
return createInputElement(el, index, "email")
case "select":
return createSelectElement(el, index)
case "date":
return createDateElement(el, index)
case "upload":
return createUploadElement(el, index)
}
}
const handleInputChange = () => {
}
const createInputElement = (data, index, type) => {
if(type && type==="email") {
return (
<Controller
name={data.fieldType}
control={control}
defaultValue=""
render={(props) => <TextField
{...props}
{...register}
onChange={handleInputChange}
variant="standard"
fullWidth
error={!!errors.email}
helperText={errors.email? errors.email?.message: ''}
key={index}
label={data.label}
color="secondary"
sx={{marginBottom: '1rem'}}
/>}
/>
)
} else if (type && type === "password") {
return (
<Controller
name={data.fieldType}
control={control}
defaultValue=""
render={(props) => <TextField
{...props}
{...register}
variant="standard"
fullWidth
type="password"
error={!!errors.password}
helperText={errors.password? errors.password?.message: ''}
key={index}
label={data.label}
color="secondary"
sx={{marginBottom: '1rem'}}
/>}
/>
)
} else if (type && type === "textInput") {
return (
<Controller
name={data.fieldType}
control={control}
defaultValue=""
render={(props) => <TextField
{...props}
{...register}
variant="standard"
fullWidth
error={!!errors.textInput}
helperText={errors.textInput? errors.textInput?.message: ''}
key={index}
label={data.label}
color="secondary"
sx={{marginBottom: '1rem'}}
/>}
/>
)
} else if (type && type === "numberInput") {
return (
<Controller
name={data.fieldType}
control={control}
defaultValue=""
render={(props) => <TextField
{...props}
{...register}
variant="standard"
fullWidth
error={!!errors.numberInput}
helperText={errors.numberInput? errors.numberInput?.message: ''}
key={index}
label={data.label}
color="secondary"
sx={{marginBottom: '1rem'}}
/>}
/>
)
}
}
const createSelectElement = (data, index) => {
return (
<Select
id="demo-simple-select-standard"
value={""}
onChange={handleChange}
label={data.label}
sx={{marginBottom: '1rem'}}
options={data.options}
/>
)
}
const createDateElement = (data,index) => {
return (
<Box
key={index}
display={'flex'}
justifyContent={'start'}
marginBottom={2}>
<DatePicker
label={data.label}
value={new Date()}
onChange={(e) => console.log(e)}/>
</Box>)
}
const createUploadElement = (data, index) => {
return (
<Box
key={index}
display={'flex'}
justifyContent={'start'}
marginBottom={'1rem'}>
<TextField
variant="standard"
className={classes.textField}
type="file"
accept="image/*"
label={data.label}
InputProps={{
endAdornment: (
<InputAdornment position="start">
<CloudUpload />
</InputAdornment>
),
register
}}
// onChange={onChange}
/>
</Box>
)
}
const handleChange = () => {
console.log('')
}
const handleFormSubmission = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(handleFormSubmission)}>
{formData.map((el, index) => (
createFormElement(el, index)
))}
<Button type="submit" variant="contained">Submit</Button>
</form>
)
}
[
{
"label": "Full Name",
"required": "true",
"fieldType": "textInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"fullName": ""}
},
{
"label": "Mobile Number",
"required": "true",
"fieldType": "numberInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"mobileNumber": ""}
},
{
"label": "Email",
"required": "true",
"fieldType": "email",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"email": ""}
},
{
"label": "Product",
"required": "true",
"fieldType": "select",
"options": [{
"carLoan": "Car Loan",
"homeLoan": "Home Loan",
"goldLoan": "Gold Loan"
}],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"selectedValue": ""}
},
{
"label": "Product Schema",
"required": "true",
"fieldType": "select",
"options": [{
"test1": "Test 1",
"test2": "Test 2",
"test3": "Test 3"
}],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"selectedValue": ""}
},
{
"label": "Loan Amount",
"required": "true",
"fieldType": "numberInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"loanAmount": ""}
},
{
"label": "Upload Adhaar",
"required": "true",
"fieldType": "upload",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"uploadAdhaar": ""}
},
{
"label": "Upload Pan",
"required": "true",
"fieldType": "upload",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"uploadPan": ""}
},
{
"label": "Callback Appointment",
"required": "true",
"fieldType": "date",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"callbackAppointment": ""}
},
{
"label": "Branch Name",
"required": "true",
"fieldType": "textInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"callbackAppointment": ""}
},
{
"label": "Created By",
"required": "true",
"fieldType": "textInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"callbackAppointment": ""}
},
{
"label": "Assigned Person",
"required": "true",
"fieldType": "select",
"options": [{
"test1": "Test 1",
"test2": "Test 2",
"test3": "Test 3"
}],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"assignedPerson": ""}
},
{
"label": "Description",
"required": "true",
"fieldType": "textInput",
"options": [],
"textReturn": "true",
"hasIcon": "false",
"fieldKey": {"description": ""}
}
]
你可以使用
const [state, setState] = useState({});
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
用于更新多个子值。如果你不知道后端需要多少字段,你可以使用这个,如果你在后端添加任何字段,那么你不必为它创建新的状态,这段小代码将工作。
检查这是否对你有帮助。