antd disable field based on checkbox - reactjs

I'm using dynamic form in ant design library
I create checkbox and What I want to do is disable this timepicker field based on
the checkbox is checked or not
how can I do this
const Demo = () => {
const onFinish = (values) => {console.log(values)};
return (
<Form
>
{/* dynamically created card */}
<Form.List name="except">
{(fields, { add, remove }) => (
{fields.map((field) => (
<Space key={field.key}>
<MinusCircleOutlined
onClick={() =>remove(field.name)}/>
<Form.Item
{...field}
valuePropName={[field.name,'checked']} className="mb-3"
>
<Checkbox>مغلق</Checkbox>
</Form.Item>
{/* ------------- from_hour -------------*/}
<Form.Item
{...field}
name={[field.name,'from_hour']}
fieldKey={[field.fieldKey,'from_hour']}
>
<TimePicker
use12Hours
placeholder="اختر"
format="hh:mm A"
/>
</Form.Item>
</Space>
))}
)}
</Form.List>
</Form>
);
};

You need state. You can control the checked value of the checkbox and use that state to conditionally render another form item.
There is an example of this behavior in the docs.

Related

React Hooks - Input loses focus when adding or removing input fields dynamically

I have a form displayed in a modal window. This form is divided into several tabs. One of them has two grouped field: a dropdown list countries and a description textfield. There is an "Add button" which allows to create a new grouped field.
The problem is that each time, I filled the textfield, i lost the focus, because the form is re-rendered. I tryed to move the form outside of the default function but i still have the same issue.
I also set unique keys to each element, but still.
I know there is a lot of documentation of this, but despite this, its not working. I could set the autofocus, but when there is more than a one group field, the focus will go to the last element.
I am using Material UI (react 17)
Anyway, below is the code (which has been truncated for a better visibility) :
function GeoForm (props) {
return(
<React.Fragment>
<Autocomplete
id={"country"+props.i}
style={{ width: 300 }}
options={Object.keys(countries.getNames('fr')).map(e => ({code: e, label: countries.getNames('fr')[e]}))}
getOptionSelected={(option, value) => (option.country === value.country)}
classes={{
option: props.classes.option,
}}
defaultValue={props.x.country}
key={"country"+props.i}
name="country"
onChange={(e,v) => props.handleInputGeoCountryChange(e, v, props.i)}
getOptionLabel={(option) => (option ? option.label : "")}
renderOption={(option) => (
<React.Fragment>
{option.label}
</React.Fragment>
)}
renderInput={(params) => (
<TextField
{...params}
label="Choose a country"
variant="outlined"
inputProps={{
...params.inputProps,
autoComplete: 'new-password', // disable autocomplete and autofill
}}
/>
)}
/>
<TextField
id={"destination"+props.i}
onChange={e => props.handleInputGeoDestinationChange(e, props.i)}
defaultValue={props.x.destination}
name="destination"
key={"destination"+props.i}
margin="dense"
label="Destination"
type="text"
/>
{props.inputGeoList.length !== 1 && <button
className="mr10"
onClick={() => props.handleRemoveGeoItem(props.i)}>Delete</button>}
{props.inputGeoList.length - 1 === props.i &&
<Button
onClick={props.handleAddGeoItem}
variant="contained"
color="primary"
//className={classes.button}
endIcon={<AddBoxIcon />}
>
Add
</Button>
}
</React.Fragment>
)
}
export default function modalInfo(props) {
const classes = useStyles();
const [openEditDialog, setOpenEditDialog] = React.useState(false);
const handleAddGeoItem = (e) => {
console.log(e);
setInputGeoList([...inputGeoList, { country: "", destination: "" }]);
};
// handle input change
const handleInputGeoCountryChange = (e, v, index) => {
const list = [...inputGeoList];
list[index]['country'] = v;
setInputGeoList(list);
};
const handleInputGeoDestinationChange = (e, index) => {
const { name, value } = e.target;
console.log(name);
const list = [...inputGeoList];
list[index][name] = value;
setInputGeoList(list);
console.log(inputGeoList)
};
// handle click event of the Remove button
const handleRemoveGeoItem = index => {
const list = [...inputGeoList];
list.splice(index, 1);
setInputGeoList(list);
};
const TabsEdit = (props) => {
return(
<div className={classes.root}>
<form className={classes.form} noValidate onSubmit={onSubmit}>
<Tabs
orientation="vertical"
variant="scrollable"
value={value}
onChange={handleChange}
aria-label="Vertical tabs example"
className={classes.tabs}
>
[...]
<Tab label="Geo-targeting" {...a11yProps(4)} disableRipple />
</Tabs>
[...]
</TabPanel>
<TabPanel value={value} index={4}>
{
inputGeoList.map((x, i)=>{
return(
<GeoForm
inputGeoList={inputGeoList}
x={x}
i={i}
handleRemoveGeoItem={handleRemoveGeoItem}
handleInputGeoDestinationChange={handleInputGeoDestinationChange}
handleInputGeoCountryChange={handleInputGeoCountryChange}
handleAddGeoItem={handleAddGeoItem}
handleInputGeoDestinationChange={handleInputGeoDestinationChange}
classes={classes}
/>
)
})
}
</TabPanel>
<TabPanel value={value} index={5}>
Mobile-targeting
</TabPanel>
<DialogActions>
<Button onClick={props.handleClose} color="primary">
Annuler
</Button>
<Button type="submit" color="primary">
Enregistrer
</Button>
</DialogActions>
</form>
</div>
)
}
return (
<div>
<div>
<EditIconButton onClickEdit={() => setOpenEditDialog(true)} />
</div>
<div>
<EditDialog open={openEditDialog} handleClose={() => setOpenEditDialog(false)} >
<TabsEdit/>
</EditDialog>
</div>
</div>
);
codesandbox
Any help or suggestion are welcome. Thank you
TL;DR: Your TabsEdit component was defined within another component, thus React was remounting it as a new component each time, making the focused state to be lost. This Pastebin fixes your code, it maintains the focus as you type.
NL;PR: I suffered from this same issue for months, the props are not the only reference checked for reconciliation, the component's memory ref is too. Since the component's function ref is different each time, React process it as a new component, thus unmounting the previous component, causing the state to be lost, in your case, the focus.

React Final Form and Material UI, disabling element when specific radio button is selected

New to React Final Form and creating a form with Material UI. I have a date picker disabled by default and want to enable it when a certain radio is selected. I am unsure how to get the form to enable the picker when the 'schedule' radio button is selected and disable it when not.
My Radio group component:
<RadioGroup defaultValue={defaultVal} name={name}>
{items.map(item => (
<React.Fragment key={item.value}>
<FormControlLabel
name={item.name}
label={item.label}
value={item.value}
control={
<Field
name={name}
type="radio"
render={({ input: { name, label, value, onChange } }) => (
<Radio
name={name}
label={label}
value={value}
onChange={onChange}
/>
)}
/>
}
/>
</React.Fragment>
))}
</RadioGroup>
Form Component:
const items=[
{
name: 'send',
value: 'immediately',
label: 'Immediately
},
{
name: 'send',
value: 'schedule',
label: 'Schedule
}
]
<Form onSubmit={onSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Schedule items={items} />
...
</form>
)}
</Form>
My Schedule Component:
<RadioGroup name="options" defaultValue="immediately" items={items}/>
<TimePicker disabled={true} />
You need to get radio field state in Schedule component. Check this link from final form docs: https://codesandbox.io/embed/pyrwplknom?codemirror=1

Using React Hook Form with Material UI Select Component

I've been having trouble finding the correct way to use React Hook Form with certain Material UI components. I can get it to work with simple Text Fields but when it comes to nested components I can't figure it out.
Specifically, I am trying to submit the data from the selection in a Select component with child MenuItems.
See the notes in the code:
export default function NewGoalPane() {
const classes = useStyles();
const {register, handleSubmit} = useForm();
return (
<div className={classes.root}>
<CssBaseline />
<form noValidate onSubmit={handleSubmit((data) => alert(JSON.stringify(data)))}>
<main className={classes.content}>
<div className={classes.text_field_section}>
//This text field works and React Hook Form reads the data correctly.
<TextField
label="Goal Title"
name="goalTitle"
defaultValue=""
inputRef={register}/>
</div>
//This Select component does not read the data from the selection of the MenuItems.
<div className={classes.section}>
<Select
label="Repeating"
name="repeating"
defaultValue={true}
inputRef={register} // Here I call register like all the React Hook Form docs say
>
<MenuItem value={true}>Yes</MenuItem>
<MenuItem value={false}>No</MenuItem>
</Select>
</div>
</main>
</form>
</div>
);
}
How do I fix the Select component so that React Hook Form collects the data in the form submission?
I found Material UI's TextField simple as it requires less code and also you can avoid using controller and Select component. This is my solution.
<TextField
select
name: 'city'
inputRef={register({ required: true })}
onChange={e => setValue('city', e.target.value, {shouldValidate: true})}
label="City"
defaultValue="">
{cityList.map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
{errors.city && <ErrorText>City is required</ErrorText>}
If you are using v7 the best way was to use controllers for Material Ui components
import { useForm, Controller } from 'react-hook-form';
//component
const methods = useForm();
const { control } = methods;
<Controller
name="city"
control={control}
defaultValue=""
rules={{ required: 'City Required' }}
render={({ field: { onChange, value } }) => (
<TextField
fullWidth
label="City"
variant="outlined"
value={value}
onChange={onChange}
/>
)}
/>

Save values in a form in reactjs [duplicate]

This question already has answers here:
Save data in a form using reactjs
(2 answers)
Closed 2 years ago.
I have an application where user can submit data using a form. I'm using dynamic form where user can set how many forms he wants.
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) =>
!fieldsOnEdit.includes(index) ? (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[
{ required: true, message: "Missing first name" }
]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit{setFieldOnEdit(index)}
</Button>
</Form.Item>
</Space>
) : (
<Edit value={formVal} keyForm={index} />
)
)}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Form>
When i click on submit button appears <Edit value={formVal} keyForm={index} /> component where should appears corresponding value from the form. Now there is an issue:
when i save for example 2 forms, the second value overrides previous and so on.
I made this: value.users[value.users.length - 1].first, trying to set for every form corresponding value but it does not work.
Question: How to solve the issue, and when i will save a form to display the corresponding value in component?
demo: https://codesandbox.io/s/modest-austin-kqztw?file=/index.js:698-2310
The value is not overwritten, you are rendering always the same item.
You have the index of wanted item in keyFrom prop in your Edit component, just use it:
export const Edit = ({ value, keyForm }) => {
return (
<div>
edit mode
<p>value: {value.users[keyForm].first}</p>
</div>
);
};

Ant design <Form.List> create static array of forms

I am using antd with react to develop an application.
<Form.List name="secondaryContacts">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item
{...formItemLayoutWithOutLabel}
label={index === 0 ? "" : ""}
required={false}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
validator: phoneNumberValidator
}
]}
>
<Input
placeholder="Secondary Contact"
addonAfter={
fields.length >= 1 ? (
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
) : null
}
/>
</Form.Item>
</Form.Item>
))}
<Form.Item {...formItemLayoutWithOutLabel}>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
<PlusOutlined /> Add Secondary Contact
</Button>
</Form.Item>
</div>
);
}}
</Form.List>;
Above is the code I am using to add dynamic form fields with validation using Form.List.
Now I have a specific scenario where I need to show 7 Sliders(https://ant.design/components/slider/) for the selected time slot and store them in an array, so I thought of using Form.List and keep all sliders inside just like above(except that it's static).
But I am not clear how I can achieve it with Form.List and there are very few examples on the internet.
I have a scenario where I need to have an array of forms and club them as an array using Form.List.
You can use the initialValue property to set the static data
Example:
const staticValue = [{ formItem1: 'value1', formItem2: 'value2'}];
<Form.List name="formListName" initialValue={staticValue}>
{(fields, { add, remove }, { errors }) => (
fields.map((field) => (
<>
<Form.Item {...field} name={[field.name, 'formItem1']}>
<Input placeholder="Field 1" />
</Form.Item>
...
You still need <Form> component
Try
<Form>
<Form.List>
</Form.List>
</Form>

Resources