Validate select list with react-hook-form - javascript

I want to implement validation for React form using react-hook-form. For input value I implemented this:
<div className='input-group mb-3'>
<Controller
control={control}
name={"email"} // the key of json
defaultValue={""}
render={({ field }) => (
<input
{...field}
type="email"
className="form-control"
placeholder="Email"
aria-label="email"
onChange={(e) => field.onChange(e.target.value)}
/>
)}
/>
</div>
For me it's not clear how I need to implement it for select menu:
<div className='input-group mb-3'>
<select
className='form-control form-select'
name='budget'
id='budget'
required=''
data-msg='Please select your budget.'
>
<option value=''>Budget</option>
<option value='budget3'>More than $500,000</option>
</select>
</div>
Wat is the proper way to implement it?

somethings like this works?
<Controller
control={control}
name="budget"
rules={{
budget: {
required: "Required",
},
}}
render={({ field }) => (
<select name="budget" onChange={field.onChange} value={field.value}>
<option value="">Please select your budget</option>
<option value="budget3">More than $500,000</option>
<option value="budget2">$250,000 - $500,000</option>
<option value="budget1">$100,000 - $250,000</option>
{/* more budgets */}
</select>
)}
/>;
control is destructured from useForm like so:
const { controls, register, setValue, ...moreEls } = useForm();

Related

Cannot save data from Ant Design <Input.Group>

I tried making two input fields one for dimensions and one for weight, and both had seperate select drop down to allow the user to select a unit. I saw on Ant design docs that they had something similar, so I tried using that.
This is how I wanted it to be like:
Now i have filled my form with many other fields and they work just fine, on clicking the save button, I am not getting any data entered in the fields for dimensions or weight, nor their units. I have a standard save function which is called on 'onFinish' event:
const handleSubmit = (data) => {
console.log('data', data);
submit(data);
};
This is my code to generate the fields.
<Form onFinish={handleSubmit} >
<Row style={{ justifyContent: 'left' }}>
{<Col span={8}>
<div className="p-2 lbh-input">
<Form.Item
name="dimensions"
key="dimensions"
label="Dimensions (l x b x h)">
<Input.Group>
<Input
key='length'
name='length'
style={{ width: '15%' }}
type="number"
/>
<Input
key='breadth'
name='breadth'
style={{ width: '24%' }}
addonBefore="x"
type="number"
/>
<Input
key='height'
name='height'
style={{ width: '25%' }}
addonBefore="x"
type="number"
/>
<Select name='dimension_unit' key='dimension_unit' defaultValue="cm">
<Option value="mm">mm</Option>
<Option value="cm">cm</Option>
<Option value="inch">inch</Option>
<Option value="feet">feet</Option>
<Option value="m">m</Option>
</Select>
</Input.Group>
</Form.Item>
</div>
</Col>
}
{
<div className="p-2">
<Form.Item
key="weight"
name="weight"
label="Weight">
<Input.Group>
<Input
style={{ width: '50%' }}
type="number"
key="weight"
name="weight"
label="Weight"
className='noborderradius'
/>
<Select defaultValue="kg" name="weight_unit" key="weight_unit">
<Option value="kg">kg</Option>
<Option value="tonne">tonne</Option>
<Option value="g">g</Option>
</Select>
</Input.Group>
</Form.Item>
</div>}
</Row>
<button>SUBMIT</button>
</Form>
As you can see, i have tried using everythihg I can like label,name,key but no matter what happens, I get no data being sent no matter what I type in these two fields. What am i missing? Am i doing something wrong with <Form.item> ?
My ant design version is
"antd": "^4.3.4",
Input.Group is just for layout purposes, it does not group all your inputs into a single Form.Item. You still need to wrap all your inputs with Form.Item and attach the names there. Use the noStyle property to not override the Input.Group styling.
Also, defaultValue will give you this warning:
Warning: [antd: Form.Item] defaultValue will not work on controlled Field. You should use initialValues of Form instead.
So you can just do as it says, I've removed the styling for brevity
<Form
onFinish={handleSubmit}
initialValues={{ dimension_unit: 'cm', weight_unit: 'kg' }}
>
<Form.Item label="Dimensions (l x b x h)">
<Input.Group>
<Form.Item name="length" noStyle>
<Input type="number" />
</Form.Item>
<Form.Item name="breadth" noStyle>
<Input type="number" addonBefore="x" />
</Form.Item>
<Form.Item name="height" noStyle>
<Input type="number" addonBefore="x" />
</Form.Item>
<Form.Item name="dimension_unit" noStyle>
<Select>
<Option value="mm">mm</Option>
<Option value="cm">cm</Option>
<Option value="inch">inch</Option>
<Option value="feet">feet</Option>
<Option value="m">m</Option>
</Select>
</Form.Item>
</Input.Group>
</Form.Item>
<Form.Item label="Weight">
<Input.Group>
<Form.Item name="weight" noStyle>
<Input type="number" />
</Form.Item>
<Form.Item name="weight_unit" noStyle>
<Select>
<Option value="kg">kg</Option>
<Option value="tonne">tonne</Option>
<Option value="g">g</Option>
</Select>
</Form.Item>
</Input.Group>
</Form.Item>
<button>SUBMIT</button>
</Form>
Note that you don't need the key property.
demo: https://stackblitz.com/edit/react-41wakw?file=demo.tsx
What you can do is instead of wrapping the entire <Input.Group> with a single <Form.Item> you need to wrap each <Input> with its own <Form.Item> and give each one a unique key and name.
Working Code Sandbox
<Input.Group>
<Form.Item
name="length"
key="length"
>
<Input
style={{ width: '15%' }}
type="number"
/>
</Form.Item>
<Form.Item
key='breadth'
name='breadth'
>
<Input
style={{ width: '24%' }}
addonBefore="x"
type="number"
/>
</Form.Item>
...
</Input.Group>

How can I customised the form of Ant Design?

I am trying to make inline form design using ant design but I am not able to make it customised version.
I have attached the code, image of output from the code and what I want form should look like.
Here is the code:
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
const layoutInline ={
labelCol: {
sm: {
offset: 0,
span: 20,
},
},
wrapperCol: {
sm: {
offset: 30,
span: 30,
},
},
}
return (
<div style={{width: "70%", padding: "4%"}}>
<div>
<Form
{...layoutInline}
form={form}
layout="inline"
>
<Form.Item label="Full Name" tooltip="This is a required field">
<Input placeholder="Full Name" />
</Form.Item>
<Form.Item
label="Age"
tooltip={{
title: 'Tooltip with customize icon',
icon: <InfoCircleOutlined />,
}}
onChange={updateAge}
value={age}
>
<Input placeholder="input placeholder" />
</Form.Item>
<Form.Item name="gender" label="Gender" rules={[{ required: true }]}>
<Select
placeholder="Select an option"
onChange={updateGender}
allowClear
>
<Option value="male">male</Option>
<Option value="female">female</Option>
<Option value="other">other</Option>
</Select>
</Form.Item>
{/* <Form.Item label="Full Name" tooltip="This is a required field">
<p>{gender} {age} {ethnicity} {AST} {platelets} {ASTupper} {ALT} {HBVDNA} {report}</p>
</Form.Item> */}
<Form.Item name="Ethnicity" label="Ethnicity" rules={[{ required: true }]}>
<Select
placeholder="Select an option"
onChange={updateEthnicity}
allowClear
>
<Option value="South-East-Asian">South East Asian</Option>
<Option value="South-Asian">South-Asian</Option>
<Option value="African">African</Option>
<Option value="Other">Other</Option>
</Select>
</Form.Item>
</Form>
</div>
It is coming out on the page like this.
What I want is that should look like this.
Can anyone guide me, how to achieve it?
1 - The value of offset and span follow the rulesĀ of Antd Grid Layout system and it cannot be more than 24 (24 columns).
2 - AddinglabelCol and wrapperCol on the Form element will apply the same layout for Every fields. As you'r desired deisgn, each fields has a different layout, so you'll need to apply it on each
3 - layout="inline" on Form means that they will just be all inline
Finally, the layout system on Antd Form is good to have vertically aligned fields. If you want to have a full control of the field display, you have to wrap each field your self and use custom style or Antd Grid columns.
You may need something like this :
https://codesandbox.io/s/form-methods-antd4123-forked-xo3zp?file=/index.js:922-2483
<Form form={form}>
<Row>
{* sm is only a breakpoint, you may need to add some more for better responsive *}
<Col sm={14}>
<Form.Item label="Full Name" tooltip="This is a required field">
<Input placeholder="Full Name" />
</Form.Item>
</Col>
<Col sm={{ offset: 2, span: 8 }}>
<Form.Item label="Age">
<Input placeholder="input placeholder" />
</Form.Item>
</Col>
</Row>
<Row>
<Col sm={8}>
<Form.Item
name="gender"
label="Gender"
rules={[{ required: true }]}
>
<Select placeholder="Select an option" allowClear>
<Option value="male">male</Option>
<Option value="female">female</Option>
<Option value="other">other</Option>
</Select>
</Form.Item>
</Col>
<Col sm={{ offset: 2, span: 8 }}>
<Form.Item
name="Ethnicity"
label="Ethnicity"
rules={[{ required: true }]}
>
<Select placeholder="Select an option" allowClear>
<Option value="South-East-Asian">South East Asian</Option>
<Option value="South-Asian">South-Asian</Option>
<Option value="African">African</Option>
<Option value="Other">Other</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Form>
In my opinion try to avoid in-line style, add className to every component (even from Ant.) then in the CSS play with position (looks like you want flex and margin/padding)

How to validate select input field using React Hook Form?

I'm trying to validate a form made using form fields from React MD by using React Hook Form. The text input fields are working fine.
But the validations aren't working on the select field. Here is the code:
<Controller
control={control}
name="salutation"
defaultValue=""
rules={{ required: "Salutation is required" }}
disabled={isSubmitting}
render={(props) => (
<Select
id="salutation"
{...props}
label="Salutation"
options={SALUTATION_ITEMS}
value={salutationValue}
onChange={(e) => handleSalutationChange(e)}
disableLeftAddon={false}
rightChildren={
<RiArrowDownSFill className="dropDownArrow" />
}
/>
);
}}
/>
The error persists even after the user selects a value:
{errors.salutation && (
<div className="validation-alert msg-error ">
<p>{errors.salutation.message}</p>
</div>
)}
I'm probably missing something or doing something wrong.
I think you are missing props.value and props.onChange(e) and you may not need handleSalutationChange(e):
<Controller
control={control}
name="salutation"
defaultValue=""
rules={{ required: "Salutation is required" }}
disabled={isSubmitting}
render={(props) => (
<Select
id="salutation"
{...props}
label="Salutation"
options={SALUTATION_ITEMS}
value={props.value} // This one: props.value
onChange={(e) => {
props.onChange(e) // I think you are missing this
handleSalutationChange(e) // NOT Needed ?
}}
disableLeftAddon={false}
rightChildren={
<RiArrowDownSFill className="dropDownArrow" />
}
/>
);
}}
/>

React - How to get input from looped <form>?

I am new on React.
I have a condition which is to loop the form, please help me
Here is the code:
this.state.products.map(product => {
<form onSubmit={this.handleSubmit}>
<select name="size" className="form-control" style={{height: '46px;'}}>
<option key="1" value="1">Red</option>
<option key="2" value="2">Yellow</option>
<option key="3" value="3">Green</option>
</select>
<input type="submit" value="Pick This" className="form-control" onClick={() => this.handleSubmit} />
</form>
});
If there was 3 form, how to get the selected value from clicked submit button form?
Or is there another simple way?
Thank you
One method would be changing how your onSubmit function is handled.
So you could pass which index of products you are submitting like so
this.state.products.map((product, i) => {
<form onSubmit={event => this.handleSubmit(event, i)}>
<select name="size" className="form-control" style={{height: '46px;'}}>
<option key="1" value="1">Red</option>
<option key="2" value="2">Yellow</option>
<option key="3" value="3">Green</option>
</select>
<input type="submit" value="Pick This" className="form-control" onClick={() => this.handleSubmit} />
</form>
});
It also looks like your form is uncontrolled, which another possibly is having the select change a value in state.
<select name="size" onChange={e => this.handleChange(e, i)} className="form-control" style={{height: '46px;'}}>
<option key="1" value="1">Red</option>
<option key="2" value="2">Yellow</option>
<option key="3" value="3">Green</option>
</select>
and in your handleChange, you would change a value in state that would correspond to the product from your state.
You can use a ref to get form values from the DOM.
Here you need one ref per product, so you could use the index of product to save the ref and also to submit de form.
class Example extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
this.selectByProduct = {};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event, productIndex) {
event.preventDefault();
const size = this.selectByProduct[productIndex].value;
console.log(`you submitted the size ${size} of product ${productIndex}`)
}
render() {
return this.state.products.map((product, i) => (
<form onSubmit={event => this.handleSubmit(event, i)}>
<select ref={select => this.selectByProduct[i] = select} name="size" className="form-control" style={{height: '46px;'}}>
<option key="1" value="1">Red</option>
<option key="2" value="2">Yellow</option>
<option key="3" value="3">Green</option>
</select>
<input type="submit" value="Pick This" className="form-control" />
</form>
));
}
}

How to make a 'Select' component as required in Material UI (React JS)

I want to display like an error with red color unless there is a selected option.
Is there any way to do it.
For setting a required Select field with Material UI, you can do:
class SimpleSelect extends React.PureComponent {
state = {
selected: null,
hasError: false
}
handleChange(value) {
this.setState({ selected: value });
}
handleClick() {
this.setState(state => ({ hasError: !state.selected }));
}
render() {
const { classes } = this.props;
const { selected, hasError } = this.state;
return (
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl} error={hasError}>
<InputLabel htmlFor="name">
Name
</InputLabel>
<Select
name="name"
value={selected}
onChange={event => this.handleChange(event.target.value)}
input={<Input id="name" />}
>
<MenuItem value="hai">Hai</MenuItem>
<MenuItem value="olivier">Olivier</MenuItem>
<MenuItem value="kevin">Kevin</MenuItem>
</Select>
{hasError && <FormHelperText>This is required!</FormHelperText>}
</FormControl>
<button type="button" onClick={() => this.handleClick()}>
Submit
</button>
</form>
);
}
}
Working Demo on CodeSandBox
Material UI has other types of Select(native) also where you can just use plain HTML required attribute to mark the element as required.
<FormControl className={classes.formControl} required>
<InputLabel htmlFor="name">Name</InputLabel>
<Select
native
required
value={this.state.name}
onChange={this.handleChange}
inputProps={{
name: 'name',
id: 'name'
}}
>
<option value="" />
<option value={"lala"}>lala</option>
<option value={"lolo"}>lolo</option>
</Select>
</FormControl>
P.S. https://material-ui.com/demos/selects/#native-select
The required prop only works when you wrap your elements inside a <form> element, and you used the submit event to submit the form.
this is not related to react, this is pure HTML.
In MUI v5 (2022), it works like this:
const handleSubmit = (e)=>{
e.preventDefault()
// ...
}
return (
<form onSubmit={handleSubmit}>
<Select required value={val} onChange={handleChange} required>
<MenuItem value="yes">Yes</MenuItem>
<MenuItem value="no">No</MenuItem>
</Select>
<Button type="submit">Submit</Button>
</form>
)
As you can see, it works the same way you think it should work, so what your code should probably be similar to this.
But the required prop only works when you wrap your elements inside a element, and you used the submit event to submit the form.
And if you're using <FormControl>, but the required prop on both elements:
<FormControl required>
<Select required>
// ...
</FormControl>

Categories

Resources