I am required to build a Form Builder component that builds forms based on the data that's coming from the backend in json format.. The Json is given below after description.. The Form component can have any number and type of fields.. As a first step I narrowed down the number of field types i might encounter. So I figured that in my case I will be dealing with 7 types of inputs->
- password
- select
- file
- date
- input of type text
- input of type number
So .. Now i can create form elements on the basis of whatever fieldType attribute's value i am receiving from json... Everything is good, if there will only be one field of a particular field type. Because i can declare 7 state variables one for each formField type to store their values and perform form validations.
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("")
But the problem is there can be any number of a particular field type in my form- for eg: there can be more than one email type field in my form say one for customer email id and the other for vendor email id.
The Question is -> Is there any way(or is it possible) that i can know how many form fields will come from backend and based on the number of formFields and their labels(see json) create state ... Or I should use a different approach..
This is the Component I am working on->
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": ""}
}
]