I have checkbox and its working well. However I wanted the whole div to be clickable also not just the checkbox alone.
Pls check here CLICK HERE
<form onSubmit={handleSubmit}>
{products.map((product) => (
<div key={product} style={{ cursor: "pointer" }}>
{product}
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
</div>
))}
<button type="submit">Submit</button>
</form>
Wrap the checkbox in label element. This will make the text, i.e. "label" clickable as part of the input. Since label elements are display: inline you will need to also override the display CSS to maintain it as a block level element.
<form onSubmit={handleSubmit}>
{products.map((product) => (
<label key={product} style={{ cursor: "pointer", display: "block" }}>
{product}
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
</label>
))}
<button type="submit">Submit</button>
</form>
If you want to have your product name clickable and change checkbox state, use a label with an htmlFor
You would need to make a few changes to your app like so:
<label for={product}>{product}</label> <!-- ADD LABEL TAG AND FOR HERE! <!--
<Checkbox
name="products"
id={product} // <-- HAVE YOUR ID SAME WITH THE FOR ABOVE
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>;
And inside Input component:
<Input
type="checkbox"
id={id} // <-- CHANGE ID TO USE ACTUAL ID ABOVE
name={name}
value={value}
checked={checked}
onChange={onChange}
/>;
Related
I wanted to enter the word Product but when I submit it, it does not show up in the console.
What shows up in the console:
As you can see here, the word Product does not appear in the console. Any idea on how I can solve this?
This is the codesandbox link: https://codesandbox.io/s/react-hook-form-usefieldarray-nested-arrays-forked-vjwbp?file=/src/index.js
this is the fieldArray.js where the input fields for products is
import React from "react";
import { useFieldArray } from "react-hook-form";
import NestedArray from "./nestedFieldArray";
import { TextField } from "#mui/material";
let renderCount = 0;
export default function Fields({ control, register, setValue, getValues }) {
const { fields, append, remove, prepends } = useFieldArray({
control,
name: "test"
});
renderCount++;
return (
<>
<ul>
{fields.map((item, index) => {
return (
<li key={item.id}>
{/* <select
ref={register()}
name={`test[${index}].name`}
defaultValue={item.name}
>
<option value="">Select</option>
<option value="10">ItemA</option>
<option value="20">ItemB</option>
</select> */}
{/* {index + 1} to show the qty */}
<TextField
name={`test[${index}].name`}
refer={register()}
defaultValue={item.name}
/>
<button type="button" onClick={() => remove(index)}>
Delete
</button>
<NestedArray nestIndex={index} {...{ control, register }} />
</li>
);
})}
</ul>
<section>
<button
type="button"
onClick={() => {
append({ name: "append" });
}}
>
Add product
</button>
</section>
<span className="counter">Render Count: {renderCount}</span>
</>
);
}
you can change it try way:
fieldArray.js
<TextField
name={`test[${index}].nestedArray[${index}].product`}
inputRef={register({ required: true })}
defaultValue={item.name}
/>;
export const DefaultColumnFilter11 = ({
column: {
filterValue,
setFilter,
preFilteredRows: { length },
},
}) => {
const [condVal, setcondVal] = useState("");
return (
<div>
<input
type="radio"
value=">"
name="cond"
onChange={(event) => setcondVal(event.target.value)}
/>{" "}
<
<br />
<input
type="radio"
value="<"
name="cond"
onChange={(event) => setcondVal(event.target.value)}
/>
>
<br />
<Input
value={filterValue|| ""}
onChange={(e) => {
setFilter( {condVal} && (e.target.value || undefined));
}}
placeholder={`${length}`}
/>
</div>
);
};
This is the code and I want to filter the column using radio button condition greater than ">" and less than "<" that should be applied to the input search.
Essentially I have some data that I would like to filter using radio buttons.
Selecting a button of a particular value should only cause those objects with that value as their property to be rendered.
I have a list of items mapped to be displayed on my "allItems" page. Now every Item has a button that fires up a modal with the specs of the item, but this modal display the same value for all the item (the last one in the array).
I tried to pass the id in toggle func but it doesn't work.
Anyone knows how can I display the same data in the Card and the Modal?
Here's my code:
state = {
modal: false,
}
toggle = () => {
this.setState({
modal: !this.state.modal
})
}
render(){
return(
{rooms.map(({ _id, name, descr, prezzo }) => (
<>
<Card key={_id} className="rooms-card-template">
<CardImg />
<CardBody>
<CardTitle>{name}</CardTitle>
<CardText>{descr}</CardText>
<CardSubtitle>{prezzo}$/notte</CardSubtitle>
<Button onClick={this.toggle}>Apri Annuncio</Button>
<Modal isOpen={this.state.modal} toggle={this.toggle}>
<ModalHeader >{name}</ModalHeader>
<ModalBody>
{descr}
<h5 style={{ paddingTop: "10px"}}>Riepilogo prenotazione</h5>
<Form>
<FormGroup>
<Label>Struttura:</Label>
<Input value={name}/>
</FormGroup>
<FormGroup>
<Label>Ospiti:</Label>
<Input type="select"name="ospiti" id="ospiti">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
</Input>
</FormGroup>
<FormGroup>
<Label>Check in</Label>
<Input type="date" name="checkin-date" id="checkin-date" placeholder="Check in" />
</FormGroup>
<FormGroup className="rooms-checkout">
<Label>Check out</Label>
<Input type="date" name="checkout-date" id="checkout-date" placeholder="Check out" />
</FormGroup>
{ isAuth ? userAuth : userUnauth }
</Form>
</ModalBody>
</Modal>
</CardBody>
</Card>
</>
))}
)
}
Issue
You've a single boolean modal state that all the modals cue from. When this.state.modal is true then a modal is rendered and opened for each element being mapped.
Solution
Instead of storing a boolean value for whether or not a modal should be open you should store an id when you want a specific modal to open.
state = {
modal: null // <-- null, non-id state value
};
toggle = (id) => () => { // <-- curried function handler
this.setState((prevState) => ({
modal: prevState.modal === id ? null : id // <-- set new id or toggle off
}));
};
render() {
return (
<>
{rooms.map(({ _id, name, descr, prezzo }) => (
<Card key={_id} className="rooms-card-template">
...
<CardBody>
...
<Button
onClick={this.toggle(_id)} // <-- pass id
>
Apri Annuncio
</Button>
<Modal
isOpen={this.state.modal === _id} // <-- open if id is match
toggle={this.toggle(_id)} // <-- pass id
>
...
</Modal>
</CardBody>
</Card>
))}
</>
);
}
Error Message:
Error: Cannot read property 'map' of undefined
React component:
const checkBox = props => {
return (
<div>
<label htmlFor={props.name} className='form-label'>
{props.title}
</label>
<div className='checkbox-group'>
{
props.options.map(option => {
return (
<label key={option}>
<input
className='form-checkbox'
id={props.name}
name={props.name}
onChange={props.handleChange}
value={option}
checked={props.selectedOptions.indexOf(option) > -1}
type='checkbox'
/>{' '}
{option}
</label>
)
})
}
</div>
</div>
)
}
In my code is anything going wrong? If yes please let me know. Can someone help me out from this?
The error
Cannot read property 'map' of undefined
Is thrown when the map function in the comment list component is executed.
A question comment references a tutorial showing how to build a form and its sub-components. A couple of the components include examples of how to pass props, but that's missing from <Checkbox/>.
To fill that gap, here's an example of how you might expect to use <Checkbox/>. I didn't read the article in its entirety, so I'm hoping you can help correct any mistakes I've made here and it gets you started on your own development.
<Checkbox
title={'Skills'}
name={'skills'}
options = {this.state.skills} <!-- array of skills or empty array -->
selectedOptions = {this.state.newUser.skills} <!-- array of skills or empty array -->
handleChange = {this.handleInput}
/>
You could also use a simple fallback like.
const checkBox = props => {
const { options, name, titlem, selectedOptions, handleChange } = props
return (
<div>
<label for={name} className="form-label">
{title}
</label>
<div className="checkbox-group">
{(options || []).map(option => {
return (
<label key={option}>
<input
className="form-checkbox"
id={name}
name={name}
onChange={handleChange}
value={option}
checked={selectedOptions.indexOf(option) > -1}
type="checkbox"
/>{" "}
{option}
</label>
);
})}
</div>
</div>
);
};
const checkBox = ({ options, name, title, selectedOptions, handleChange }) => {
return (
<div>
<label htmlFor={name} className='form-label'>
{title}
</label>
<div className='checkbox-group'>
{
options && options.map(option => {
return (
<label key={option}>
<input
className='form-checkbox'
id={name}
name={name}
onChange={handleChange}
value={option}
checked={selectedOptions.indexOf(option) > -1}
type='checkbox'
/>{' '}
{option}
</label>
)
})
}
</div>
</div>
)
}
I've got my custom icon toggle component:
class ToggleIconSwitch extends Component {
render() {
const { leftIcon, rightIcon, name, handleOptionChange, checked, leftValue, rightValue } = this.props
return (
<div className="toggle-switch-icon-wrapper">
<div className="toggle-switch">
<input
type="radio"
name={ name }
id={ leftValue }
value={ leftValue }
checked={ checked === leftValue }
onChange={ handleOptionChange }
/>
<label htmlFor={ leftValue } className="icon">{ leftIcon }</label>
<input
type="radio"
name={ name }
id={ rightValue }
value={ rightValue }
checked={ checked === rightValue }
onChange={ handleOptionChange }
/>
<label htmlFor={ rightValue } className="icon">{ rightIcon }</label>
</div>
</div>
)
}
}
and then I've got multiple toggles on some page and I want to handle each toggle input change...
_.map(acl, (permission) => {
return (
<div className="row" key={ permission.id } >
<ToggleIconSwitch
name={ _.toString(permission.id) }
leftIcon={ <VisibilityIcon className="svg" /> }
leftValue="read"
rightIcon={ <CreateIcon className="svg"/> }
rightValue="write"
checked={ permission.permission }
handleOptionChange={ this.toggleUsersPermission }
/>
<IconButton
aria-label="Delete"
className="icon-button animate"
onClick={ this.openDeleteDialog(permission) }
>
<CloseIcon className="icon-button-red" />
</IconButton>
</div>
)
})
The problem is that the toggleUsersPermission function is always called only for the first ToggleIconSwitch component, even if I click on another ToggleIconSwitch. It also does not react when I click on the other toggle component's label, which is checked in first toggle component in list. I'm always getting event.target of the first toggle in list ... Why?
Try to click on the icons in CodeSandbox here
The problem was in labels, they have all equal htmlFor value and also input ids were same