React material UI Select box onBlur event not working - reactjs

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

Related

React MUI - Clearing input on Autocomplete component

I have an Autocomplete component which displays the coutries name and flags as in the example from the MUI doc.
My goal is simply the following: once the Autocomplete component is clicked, the country's name must be cleared displaying only the placeholder.
I achieved this with a simple onClick event in the renderInput which triggers the following function:
const handleClear = (e) => {
e.target.value = "";
};
If trying the code everything works as expected, apparently.
Actually, the clearing happens only when the country's name is clicked, but if a different portion of the component is clicked, like the flag or the dropdown arrow, the country's name is simply focused, not cleared.
In short, here the current behaviour:
and here the expected behaviour:
Is there a way to fix this?
That's behavior occurs because when you click on the flag, the e.target won´t be the input element, but the wrapper div. You can see this just adding a console.log to the handleClear function:
const handleClear = (e) => {
console.log("clicked TARGET ELEMENT: ", e.target);
// If you click on the input, will see:
// <input ...
// And if you click on the flag, you will see:
// <div ...
};
If you want to control the input state value and the text value separately, you probably should go with the two states control - check it on MUI docs.
The code will be something like:
export default function CountrySelect() {
const [value, setValue] = useState(null);
const [inputValue, setInputValue] = React.useState("");
const handleClear = (e) => {
console.log("clicked TARGET ELEMENT: ", e.target);
setInputValue("");
};
return (
<Autocomplete
id="country-select-demo"
disableClearable
value={value}
onChange={(event, newValue) => {
setValue(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
openOnFocus
sx={{ width: 300 }}
options={countries}
autoHighlight
getOptionLabel={(option) => option.label}
renderOption={(props, option) => (
<Box
component="li"
sx={{ "& > img": { mr: 2, flexShrink: 0 } }}
{...props}
>
<img
loading="lazy"
width="20"
src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`}
alt=""
/>
{option.label} ({option.code}) +{option.phone}
</Box>
)}
renderInput={(params) => (
<TextField
{...params}
label="Choose a country"
placeholder="Choose a country"
onClick={handleClear}
InputProps={{
...params.InputProps,
startAdornment: value ? (
<InputAdornment disablePointerEvents position="start">
<img
loading="lazy"
width="48"
src={`https://flagcdn.com/w20/${value.code.toLowerCase()}.png`}
srcSet={`https://flagcdn.com/w40/${value.code.toLowerCase()}.png 2x`}
alt=""
/>
</InputAdornment>
) : null
}}
/>
)}
/>
);
}
Instead of using onClick on TextField, you can use onOpen props and pass handleClear function in it. It works then. Selected value gets cleared whenever autocomplete is open.
Working Demo: CodeSandBox.io

material ui - Autocomplete multiple error

I am using material-ui in React.js. When using multiple in Autocomplete it gives me the error,
Uncaught TypeError: (intermediate value)(intermediate value)(intermediate value).filter is not a function at useAutocomplete,
The above error occurred in the <ForwardRef(Autocomplete)> component:
in ForwardRef(Autocomplete).
material-ui version - "#mui/material": "^5.6.0",
Code:
<Autocomplete
multiple={true}
disableCloseOnSelect
id={field.name}
name={field.name}
options={locations}
value={props.values.locationId}
size="small"
autoComplete={false}
onChange={(e, newValue) => {
props.setFieldValue(
'locationId',
newValue ? newValue : '',
true,
);
}}
onBlur={() =>
props.setFieldTouched(field.name, true)
}
getOptionLabel={(option) =>
option['name'] ? option['name'] : ''
}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</li>
)}
renderInput={(params) => (
<TextField
{...params}
fullWidth
size="small"
placeholder={field.placeholder}
variant="outlined"
/>
)}
/>
When using multiple, value must be an array (see multiple in the docs). I found this answer helpful for using a controlled Autocomplete component in multiple mode, as you're doing here.

Create tag using Autocomplete material UI when user input anything

I can type something which shows selected tags from the dropdown list but I want a user can type something and create a tag or multiple tags separated by a comma.
I used a useState hook which is an array.
const [tags, setTags] = useState([]);
I set the Autocomplete like the following code -
<Autocomplete
style={{ margin: "10px 0" }}
multiple
id="tags-outlined"
options={tags}
defaultValue={[]}
freeSolo
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
label="Tags"
placeholder="Tags"
value={tags}
onChange={(e) => setTags([...tags, e.target.value.split(",")])}
/>
)}
/>;
Surprisingly, I tried for an hour before questioning. But Solve it within a few moments after putting the question.
Here's the solution-
<Autocomplete
style={{ margin: "10px 0" }}
multiple
id="tags-outlined"
options={tags}
defaultValue={[...tags]}
freeSolo
autoSelect
onChange={(e) => setTags([...tags, e.target.value])}
renderInput={(params) => (
<TextField
{...params}
label="Tags"
placeholder="Tags"
value={tags}
/>
)}
/>;
the output will look like this.
user input tag
but I want to add multiple tags that didn't happen right now which can be put via giving a space or comma.
Partly relevant but I think it can be helpful for someone using React Hook Form library with MUI5.
Storing tags in the state would render them being saved in form data when submitted. Instead, you need to use their onChange function.
const Tags = ()=>{
const { control,handleSubmit } = useForm({
defaultValues: {
checkbox: true,
autocomplete:["test"]
}
});
const onSubmit = data => console.log(data);
return <>
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="autocomplete"
control={control}
render={({ field }) => {
const {value,onChange} = field
return <Autocomplete
style={{ margin: "10px 0" }}
multiple
id="tags-outlined"
options={value}
defaultValue={[...value]}
freeSolo
autoSelect
onChange={((e)=>onChange([...value,e.target.value]))}
renderInput={(params) => {
return <TextField
{...params}
label="Tags"
placeholder="Tags"
value={value}
/>
}}
></Autocomplete>
}
}
/>
<input type="submit" />
</form>
</>
}

Make options of Material-ui autocomplete component clickable links?

import React, { useEffect, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
export default function FreeSolo(props) {
const [vendors, setVendors] = React.useState([]);
const [value, setValue] = React.useState();
const nameRef = useRef();
useEffect(() => {
sendDataToParent();
}, [value]);
const sendDataToParent = async () => {
await props.parentFunction(value);
};
return (
<div style={{}}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
options={props.vendorData.map((option) => option.name)}
renderInput={(params) => (
<TextField
{...params}
value={value}
required
inputRef={nameRef}
onChange={(e) => {
setValue(e.target.value);
sendDataToParent();
}}
label="Vendor Name"
margin="normal"
variant="standard"
InputProps={{ ...params.InputProps, type: "search" }}
/>
)}
/>
</div>
);
}
I tried to do it using renderOption but could not get it working. I need to have the options to be clickable links so that whenever user selects of the options, he is redirected to the link.
EDIT: Solved using renderOption
renderOption={(option) => (
<React.Fragment>
<span
style={{ cursor: "pointer" }}
onClick={() => {
window.location.href = `/allvendors/${option.id}`;
}}
>
{option.name} - Click to visit the Vendor
</span>
</React.Fragment>
)}
Instead of making the options clickable links, you can redirect to the link using the onChange prop of the Autocomplete component.
I'm assuming each option in your vendorData has a name and also a link e.g.
{
name: "Google",
link: "https://www.google.com"
}
To be able to access the link from this object in the Autocomplete component's onChange, you'll need to change the options map function to return the whole option. After this change, if you try to click to open the dropdown, it will throw an error because the option label needs to be a string (e.g. the option name) and not an object (e.g. option). So, we need to add the getOptionLabel prop and return the option.name.
Finally, in the onChange function, we set the window.location.href equal to the option.link, which changes the current page's URL to the link and directs the user to that link.
<div style={{}}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
getOptionLabel={(option) => option.name}
options={props.vendorData.map((option) => option)}
onChange={(event: any, option: any) => {
window.location.href = option.link;
}}
renderInput={(params) => (
<TextField
{...params}
value={value}
required
inputRef={nameRef}
onChange={(e) => {
setValue(e.target.value);
sendDataToParent();
}}
label="Vendor Name"
margin="normal"
variant="standard"
InputProps={{ ...params.InputProps, type: "search" }}
/>
)}
/>
</div>

Material UI free solo Autocomplete, how to submit data?

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

Resources