Material UI free solo Autocomplete, how to submit data? - reactjs

I am new to Material UI and I wonder how it is possible to submit data with a free solo autocomplete component, also with a TextField. My goal is ultimately to push the router to a new page, after the search result.
I think that the code sandbox shows a more clear example:
Code Sandbox

You can just use the onChange of the autocomplete instead of tracking it with a onChange of the textfield:
export default function App() {
function handleSubmit(event, value) {
event.preventDefault();
console.log("Country:", value);
}
return (
<div className="App">
<h1>Material AutoComplete</h1>
<h2>How to get this to submit?</h2>
<div>
<Autocomplete
freeSolo
id="autocomplete"
disableClearable
options={allCountries.map((option) => option.countryname)}
onChange={handleSubmit} // This will be called on selection of the country
renderInput={(params) => (
<TextField
{...params}
margin="normal"
aria-label="enter search"
name="search"
placeholder="Search"
// No need to check the onChange here
InputProps={{
...params.InputProps,
startAdornment: <SearchIcon />,
type: "search"
}}
/>
)}
/>
</div>
</div>
);
}

Related

mui autocomplete with react-hook-form: defaultValue in formData

I'm setting the defaultValue of the Autocopmlete component as below:
<Controller
control={control}
name={name}
render={({field: {onChange, value}}) => (
<Autocomplete
freeSolo={freeSolo}
options={options}
renderInput={params => {
return <TextField {...params} label={label} margin="normal" variant="outlined" onChange={onChange} />
}}
onChange={(event, values, reason) => onChange(values)}
defaultValue={defaultValue}
/>
)}
/>
the value is well displayed based on the defaultValue.
However when I click on the submit button, the value of the Autocomplete field is always undefined if I don't use the autocomplete component.
Here are how I register the hook and component (simplified code)
const customerSchema = yup.object().shape({
route: yup.string().nullable()
})
const {control, handleSubmit} = useForm({
resolver: yupResolver(customerSchema)
})
const onSubmit = formData => {
console.log(formData) // autocomplete is undefined if no action on the autocomplete
}
<form noValidate autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
<AutoCompleteInput
control={control}
name="route"
label="route"
options={customers_routes.map(option => option.route_name)}
defaultValue={customer.route}
freeSolo={false}
/>
<Button type="submit">
Update
</Button>
</form>
What should I do to have the route field always available in the form data when I click on submit?
Why don't you use defaultValues option with useForm:
const {control, handleSubmit} = useForm({
resolver: yupResolver(customerSchema),
defaultValues: { route: customer.route },
});
and instead of sending defaultValue prop to AutoComplete you can use value prop, like this:
<Controller
control={control}
name={name}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo={freeSolo}
options={options}
renderInput={(params) => {
return (
<TextField
{...params}
label={label}
margin="normal"
variant="outlined"
onChange={onChange}
/>
);
}}
onChange={(event, values, reason) => onChange(values)}
value={value}
/>
)}
/>
Here is a simplest way to use an Autocomplete with react hook from , render your Autocomplete component inside Controller from react hook from, use onChange and value from the render function to control the value
<Controller
control={control}
name="type"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange, value } }) => (
<Autocomplete
freeSolo
options={['field', 'select', 'multiple', 'date']}
onChange={(event, values) => onChange(values)}
value={value}
renderInput={(params) => (
<TextField
{...params}
label="type"
variant="outlined"
onChange={onChange}
/>
)}
/>
)}

React material UI Select box onBlur event not working

I am using react and react material to my application. I have used select box for getting some values. If I open the selectbox, the close was not working. For that, I have decided to use onBlur event. Please see the code below
const handleBlur = () => {
setIsOpen(false);
}
<Select
multiple
MenuProps={{ PaperProps: { className: classes.menu } }}
open={isOpen}
onOpen={handleOpen}
onChange={handleChange}
onBlur={handleBlur}
margin="dense"
variant={variant}
input={inputComponent}
value={selected}
// className={classes.select}
inputProps={{
classes: {
icon: classes.icon,
}
}}
renderValue={elements => (
<div className={classes.chips}>
{(elements as string[]).map(value => {
return (
<Chip
style={{ backgroundColor: `#${ColorUtils.getColor(value)}` }}
key={value}
label={value}
className={`-select ${classes.chip}`}
/>
);
})}
</div>
)}
>
In this case, the blur was not working. Could anyone please help to fix this problem
Thanks in advance
Material UI's Select component doesn't have onBlur prop. Try onClose instead.
https://material-ui.com/api/select/#props

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}
/>
)}
/>

Google Places (multiple)

I am wanting users to be able to input multiple locations: for example
Melbourne, Perth, Sydney.
I am currently using
<input id="searchTextField"></input>
<script type="text/javascript">
function initialize() {
var options = {
componentRestrictions: {country: 'au'}
};
var input = document.getElementById('searchTextField');
var autocomplete = new google.maps.places.Autocomplete(input, options);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
However I want to move it to Material-UI Autocomplete - while yes it works it won't allow me to select the location due to Autocomplete requires options, and without that it won't work. I am wondering what is a better way to get multiple locations working.
<Autocomplete
id="searchTextField"
name="country"
options={countries}
required
multiple
variant="outlined" fullWidth
getOptionLabel={option => option.label}
// style={{ width: 300 }}
onChange={(e, value) => {
console.log(value);
setFieldValue(
"country",
value !== null ? value : initialValues.country
);
}}
renderInput={params => (
<TextField
margin="normal"
label="Country"
variant="outlined"
fullWidth
required
name="country"
{...params}
/>
)}
/>
I have also tried the following but when a user clicks it does not set to the Field.
<Field name="targetLocation" >
{({ field, form, meta }) => (
<div>
<input variant="outlined" id="searchTextField" fullWidth
type="text" {...field} required label="Campaign Name"/>
{meta.touched &&
meta.error && <div className="error">{meta.error}</div>}
</div>
)}
</Field>
DEMO of issue - you'll notice only 1 input is being sent to array.
https://codesandbox.io/s/youthful-hofstadter-dp4mx?file=/src/App.js
The problem was syncing the GoogleAutoComplete component when the state is being updated. So there was no initial value for the autocomplete.
Here is the working code.
https://codesandbox.io/s/kind-monad-cz60f?file=/src/App.js

MUI Autocomplete's 'defaultValue' not working when used with Controller of react-hook-form

I am trying to use MUI's Autocomplete with react-hook-form. I have wrapped an Autocomplete component in React Hook Form's Controller. When I try to set defaultValue to AutoComplete it does not work, when I try to change the preset value the Autocomplete component breaks.
Here is the snippet from my code.
<Controller
name="combo-box-demo"
control={control}
defaultValue={top100Films.find(film => film.year === selectedFilmYear)}
as={
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={option => option.title}
style={{ width: 300 }}
renderInput={params => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
}
/>
Sandbox link of working demo code is here.
You should add an onChange prop on the Controller and return the selected object value
Also you can then implement getOptionSelected AutoComplete
export default function ComboBox() {
const { control } = useForm({});
const [selectedFilmYear, setSelectedFilmYear] = React.useState(1994);
return (
<Controller
name="combo-box-demo"
control={control}
defaultValue={top100Films.find(film => film.year === selectedFilmYear)}
onChange={([val, obj]) => obj}
as={
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionSelected={(obj, newval) => obj.name === newval.name}
getOptionLabel={option => option.title}
style={{ width: 300 }}
renderInput={params => (
<TextField {...params} label="Combo box" variant="outlined" />
)}
/>
}
/>
);
}

Resources