Disclaimer: New to development and this is my first s/o post.
I'm using materialize on a react project with a filter that has checkboxes. I currently have the checkbox component returning as:
return (
<div className={this.props.type} >
<form>
<input
type={this.props.type}
value={label}
checked={isChecked}
onChange={(e) => this.toggleCheckboxChange(e)}
/>
<label>
<input
type={this.props.type}
value={label}
checked={isChecked}
onChange={(e) => this.toggleCheckboxChange(e)}
/>
{label}
</label>
</form>
</div>
)
It renders with the checkbox and the label next to it. However, if I remove the input tag that's outside of the label tag like so:
return (
<div className={this.props.type} >
<form>
<label>
<input
type={this.props.type}
value={label}
checked={isChecked}
onChange={(e) => this.toggleCheckboxChange(e)}
/>
{label}
</label>
</form>
</div>
)
the checkbox disappears but the text is still clickable. The component is class based so it maintains its state of being checked or unchecked.
Any ideas as to why it behaves this way?
Problem fixed! Turns out I needed an ID on my input field that matched and htmlFor property on my label field. See below code for the fix:
<div className={this.props.type} >
<form>
<input
id={label[0]}
type={this.props.type}
value={label}
checked={isChecked}
onChange={(e) => this.toggleCheckboxChange(e)}
/>
<label htmlFor={label[0]}>
{label}
</label>
</form>
</div>
Related
I have a group of checkboxes and a group if radios and I want to validate with react hook form to ensure that an error message is generated if none have been selected on submit.
I have tried experimenting with the form builder on their website but I cant work out how to validate a group of items as a single unit of validation.
<div>
<span>Option A <input type="checkbox" value="A" /></span>
<span>Option B <input type="checkbox" value="B" /></span>
<span>Option C <input type="checkbox" value="C" /></span>
</div>
<...output a validation error if one or more checkboxes hasnt been checked within the group>
<div>
<span>Option A <input type="radio" value="A" /></span>
<span>Option B <input type="radio" value="B" /></span>
<span>Option C <input type="radio" value="C" /></span>
</div>
<...output a validation error if one or more radios hasnt been checked within the group>
Is this possible and is there a correct way to do it?
Thank you for your time and attention.
You added the react-hook-form tag to your question but there is nothing in your code related to it. If indeed you're using React Hook Form a way to accomplish what you want is using schema validation through yup:
const schema = yup.object().shape({
checkbox: yup.array().min(1),
radio: yup.string().required(),
});
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<span>
Checkbox 1
<input type="checkbox" {...register('checkbox')} value="A" />
</span>
<span>
Checkbox 1
<input type="checkbox" {...register('checkbox')} value="B" />
</span>
<span>
Checkbox 3
<input type="checkbox" {...register('checkbox')} value="C" />
</span>
<p style={{ color: 'red' }}>
{errors.checkbox && 'At least one checkobox must be selected'}
</p>
<span>
<label>Radio 1</label>
<input type="radio" {...register('radio')} value="A" />
</span>
<span>
<label>Radio 2</label>
<input type="radio" {...register('radio')} value="B" />
</span>
<span>
<label>Radio 3</label>
<input type="radio" {...register('radio')} value="C" />
</span>
<p style={{ color: 'red' }}>{errors.radio && 'Radio is required'}</p>
<input type="submit" />
</form>
);
}
See a working stackblitz.
Note that as radio button options are exclusive (only one can be selected) you're just sayng that the field is required.
Check out this guide and the documentation for radio buttons
In short, giving the same name attribute to your radio buttons ensures that only one of them can be selected at a time. Adding an onChange function linked to a useState within your component allows you to track the selected value and react if none has been selected.
The resulting code should be something like this:
const YourComponent = () => {
const [myValue, setMyValue] = useState(null);
const onValueChange = e => {
setMyValue(e.target.value);
};
return (
<div>
<div onChange={onValueChange}>
<span>
Option A <input type="checkbox" value="A" name="myValue" />
</span>
<span>
Option B <input type="checkbox" value="B" name="myValue" />
</span>
<span>
Option C <input type="checkbox" value="C" name="myValue" />
</span>
</div>
{!myValue && <span>Please select a value</span>}
</div>
);
};
I have a requirement to fetch and show the values in the form text box. I need to check whether if the value exists and not equal to null then show the values in the text box otherwise show a blank field.
The existing code implementation shows something like this :
{
this.state.testJson.Names ? this.state.testJson.Names.length > 0 ? this.state.testJson.Names.map(response =>
<div className="form-group col-md-3" key={response.nameId}>
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value={response.otherName} type="text" className="form-control" id="firstName" />
</div>
):
<div className="form-group col-md-3">
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value='' type="text" className="form-control" id="firstName" />
</div> :
<div className="form-group col-md-3">
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input name="firstName" value='' type="text" className="form-control" id="firstName" />
</div>
}
I somehow feel this is not the best way to implement it as I need to avoid code repetition. Could someone tell me what is the better way to achieve this?
Thanks
There's a nice sintactic sugar for modern JavaScript and React (if you are using React, you must likely have it), you simply add a question mark before the object you're not sure it exists like:
this.state?.testJson?.Names?.length > 0
Also, you could have default values of a nullish variable like:
// names will be an empty array if it doesn't exist or if it's nullish
const names = this.state?.testJson?.Names ?? [];
All together is:
const names = this.state?.testJson?.Names ?? [];
return(
names.map(response =>
<div className="form-group col-md-3" key={response?.nameId}>
<label htmlFor="firstName">{Liferay.Language.get('first-name')}</label>
<input
name="firstName"
value={response?.otherName}
type="text"
className="form-control"
id="firstName"
/>
</div>
) : ....rest of the code!
);
You can fetching inside some cards
<div className="row">
{!names
? "Loading..."
: names.map((name) => {
return (
<div className="col">
<div className="card-body">
<h5 className="card-title">{name.firstname}</h5>
</div>
</div>
</div>
);
})}
</div>
I want to show a select tag when a checkbox is checked.
In "/register" route (which is the default route), checkbox should be unchecked by default and in "/register/tradeUser", checkbox should be checked by default.
If I use defaultChecked="true", the state of checked will not change to true.
So I want to know, how can I call setChecked(true) inside the conditional rendering?
const Register = (match) => {
const [checked, setChecked] = useState(false);
const toggleChecked = () => {
if (checked) {
setChecked(false);
} else {
setChecked(true);
}
};
return (
<form>
<input type="text" name="first-name" placeholder="First Name" />
<input type="text" name="last-name" placeholder="Last Name" />
<input type="email" name="email" placeholder="Email Address" />
<input type="password" name="password" placeholder="Password" />
<input type="password" name="confirm" placeholder="Confirm Password" />
{match.location.pathname === "/register/tradeUser" ? (
<div>
<label>
<input
type="checkbox"
name="profession"
checked={checked}
onChange={() => toggleChecked()}
/>
I am an Architect/Interior designer
</label>
<select
name="info"
placeholder="Select Option to add Architect Info"
className={`${checked ? "" : "hidden"}`}
>
<option value="certi-number">Certificate Number</option>
<option value="certificate">Registration Certificate</option>
</select>
</div>
) : (
<div>
<label>
<input
type="checkbox"
name="profession"
checked={checked}
onChange={() => toggleChecked()}
/>
I am an Architect/Interior designer
</label>
<select
name="info"
placeholder="Select Option to add Architect Info"
className={`${checked ? "" : "hidden"}`}
>
<option value="certi-number">Certificate Number</option>
<option value="certificate">Registration Certificate</option>
</select>
</div>
)}
<button>Register</button>
</form>
<label>
Existing User?
<Link to="/login" className="link">
{" Login "}
</Link>
</label>
</div>
);
};
you can easily run function in jsx like this
export default function App() {
return (
<div className="App">
{console.log(1)}
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
however you shouldn't set state in jsx because it will loop infinitely
in this case you can use useEffect
useEffect(()=>{
if(match.location.pathname=== "/register/tradeUser"){
setChecked(true)
}else{
setChecked(false)
}
},[match.location.pathname])
I have a working React component with Formik with initialValues and everything else works perfectly except for the radio box below.
The radio box is not selectable, what could be the bug?
<Field
name="accountPurpose"
render={({ field }) => (
<>
<div className="radio-item">
<input
{...field}
id="all"
value="all"
checked={field.value === "all"}
name="type"
type="radio"
/>
<label htmlFor="all"> All</label>
</div>
<div className="radio-item">
<input
{...field}
id="distribution"
value="distribution"
name="type"
checked={field.value === "distribution"}
type="radio"
/>
<label htmlFor="distribution">
Distribution
</label>
</div>
<div className="radio-item">
<input
{...field}
id="redemption"
value="redemption"
name="type"
checked={field.value === "redemption"}
type="radio"
/>
<label htmlFor="redemption">
{" "}
Redemption
</label>
</div>
</>
)}
/>
Remove checked={field.value ===...} from all inputs and use defaultChecked for just one.
like below
<input
id="all"
value="all"
name="type"
type="radio"
defaultChecked
{...field}
/>
I want dropdown with radio button which needs to show when I click field and hide when I click outside and get the selected value in the inputfield.
I need the selected value in the input field instead of placeholder value which I have and with show/hide dropdown when I click input field
<div className="inputWithIcon" onClick={this.RadioDdOnclick} id="radiobtn">
<input className="inputBlock" id="radioSelect" type="text" placeholder="choose one" />
<i className="fa fa-angle-down" />
</div>
<div className={ "BudgetRadioDd"} id={ "RadioDd"} style={{display: 'none'}}>
<fieldset>
<h4>options to choose</h4>
<div>
<label><input type="radio" id="1"/>option 1</label>
</div>
<div>
<label> <input type="radio" id="2" />option 2</label>
</div>
<div>
<label><input type="radio" id="3"/>option 3</label>
</div>
</fieldset>
</div>
Not sure how to get this done using reactjs.
How to do this in React:
You can attach a ref to the <input> to access it from the parent component, then check if the <input> is focused. If it is focused render the radio buttons, else don't.
To show the value of the selected option in the input tag, store that value in the component's state then it's easy to track its value and share that value between the input and the radios.
Also I've added the name attribute to the radio inputs so that they work properly as radio buttons.
import React, { Component } from 'react';
class FormComponent extends Component {
constructor() {
super();
this.state = {
option: '',
inputIsFocused: false
}
}
handleChange = event => {
const { value } = event.target;
this.setState({option: value});
}
toggleRadio = event => {
const { inputIsFocused } = this.state;
this.setState({inputIsFocused: !inputIsFocused});
}
render() {
const { option, inputIsFocused } = this.state;
return (
<div>
<div className="inputWithIcon" id="radiobtn">
<input ref='input' value={ option } className="inputBlock" id="radioSelect" type="text" placeholder="choose one" onFocus={ this.toggleRadio } onFocusOut={ this.toggleRadio }/>
<i className="fa fa-angle-down" />
</div>
{ inputIsFocused && (
<div className={ "BudgetRadioDd"} id={ "RadioDd"}>
<fieldset>
<h4>options to choose</h4>
<div>
<label><input name='option' type="radio" id="1" value="1" checked={ option === '1' } onChange={ this.handleChange }/>option 1</label>
</div>
<div>
<label> <input name='option' type="radio" id="2" value="2" checked={ option === '2' } onChange={ this.handleChange }/>option 2</label>
</div>
<div>
<label><input name='option' type="radio" id="3" value="3" checked={ option === '3' } onChange={ this.handleChange }/>option 3</label>
</div>
</fieldset>
</div>
)}
</div>
)
}
}
A note on the above: I think there's an issue with onFocusOut, so that doesn't actually get fired. Also of note is that the inputIsFocused value used in render is not the one from state. This is because of the issue with onFocusOut so the value of inputIsFocused needs to come from referencing the DOM, but in order to trigger a rerender, the component's state needs to update.
Here's a snippet to show what it might look like (note that the value in the snippet does not update when when a button is selected, but that functionality is present in the React code):
function hideRadio() {
const radioDd = document.querySelector('.BudgetRadioDd');
radioDd.classList.add('hidden');
}
function showRadio() {
const radioDd = document.querySelector('.BudgetRadioDd');
radioDd.classList.remove('hidden');
}
.hidden {
display: none;
}
<div class="inputWithIcon" onClick={this.RadioDdOnclick} id="radiobtn">
<input class="inputBlock" id="radioSelect" type="text" placeholder="choose one" onfocus="showRadio();" onfocusout="hideRadio();"/>
<i className="fa fa-angle-down" />
</div>
<div class="BudgetRadioDd hidden" id="RadioDd">
<fieldset>
<h4>options to choose</h4>
<div>
<label><input name='attributeName' type="radio" id="1"/>option 1</label>
</div>
<div>
<label> <input name='attributeName' type="radio" id="2" />option 2</label>
</div>
<div>
<label><input name='attributeName' type="radio" id="3"/>option 3</label>
</div>
</fieldset>
</div>
However this seems like pretty much the functionality of a <select>, so maybe consider using one of those?