How can I set onDelete prop in autocomplete? - reactjs

I want to delete chips thanks to an handleDelete function called in ChipProps but it seems not to be able to get the event.target.value... How can I do?
const handleDelete = (event) =>
{
console.log(event.target); // here I get undefined
}
<AutoComplete
multiple
id="checkboxes-options"
debug
options={newData.map((item) => item.name)}
ChipProps={{ clickable: false, onDelete: handleDelete, variant: "outlined", color: "primary" }}
disableCloseOnSelect
disableClearable
value={newData}
tag={{ color: "primary" }}
renderOption={(options, { selected }) =>
<Aux>
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 20 }}
value={options}
name={options === newData ? "isChecked" : "isUnchecked"}
color='primary'
checked={options === newData ? state.isChecked : selected}
onChange={updateNewData}
/>
}
/>
{options}
/>

Add a render function to the renderTags prop of your autocomplete component. Inside your renderTags method you can not only change your tags, but also override the onDelete function.
<Autocomplete
...
renderTags={(values) =>
values.map((value) => (
<Chip
key={value.id}
label={value.title}
onDelete={() => {
removeOption(value.id);
}}
/>
))
}
/>
Here is a reference to a similar question, which also has a demo:
Material UI Autocomplete - unchecking checkboxes in renderOption using Redux dispatch

Related

value returns 0 or undefined on MUI autocomplete unable to fetch the value in multiple attribute

On MUI Autocomplete component where the attribute is multiple, then the value prop returns 0 or undefined when we choose anyone of the selected option.
<Autocomplete
value={v.education}
onChange={handleEducationChange}
className={classes.textOutline}
multiple
id="virtualize-demo"
name="education"
style={{ width: 600 }}
disableCloseOnSelect
ListboxComponent={ListboxComponent}
options={educationList()}
getOptionLabel={(option) => option.title}
isOptionEqualToValue={(option, value) =>
option.title === value.title
}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</li>
)}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
name="education"
placeholder="Select your Qualification"
label="Education"
fullWidth
/>
)}
/>
// handleEducationChange
const initialState = { education: "", occupation: "" };
const [selectedOption, setSelectedOption] = useState([], initialState);
const handleEducationChange = (event) => {
const { name, values } = event.target;
setSelectedOption({ ...selectedOption, [name]: values });
console.log(values);
};
I need the selected value to be passed with value prop and that can be validated and posted to backend... kindly update the solution for this, Thanks.

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

v4 material ui autocomplete is not resting value?

I have the following advanced autocomplete logic in the past I used key to re render the whole component after submitting a form that is located else where any idea how to rest it the proper way ? my problem is I need to find a way to connect a value in to it a value that is connected to state some how and when my form will submit and the value will rest, (rest value will be triggered during submitting) so the autocomplete value will rest as well any idea how to integrate value using state in to this logic ?
const options = [{name : 'some option' , optionType : 'some type', tool : 'some tool'}, ...]
<Autocomplete
id='advancedselect'
ref={refAutocomplete}
options={options.sort((a, b) => -b.optionType.localeCompare(a.optionType))}
groupBy={(option: any) => option.optionType}
getOptionLabel={(option) => option.name}
renderOption={(option) => (
<React.Fragment>
<div style={{ width: '12vw', maxWidth: 75, marginRight: 10, padding: 0 }}>
<IconsOption img={option.tool} />
</div>
{option.name}
</React.Fragment>
)}
//value={val}
//onChange={(event) => setVal(event.currentTarget)}
//getOptionSelected={(option, value) => option.name === value.name}
PaperComponent={({ children }) => <Paper className={classes.backDrop}>{children}</Paper>}
PopperComponent={(props: any) => <Popper open={open.open} anchorEl={open.anchorEl} {...props} className={classes.popper} placement='bottom'></Popper>}
onClose={() => setOpen({ /*anchorEl: null,*/ ...open, open: false })}
onOpen={(event: any) => {
const { currentTarget } = event;
setOpen({
anchorEl: currentTarget,
open: true,
});
}}
onInputChange={handleAutoComplete}
className={classes.root}
renderInput={(params) => (
<TextField
name='field'
error={err.exercise ? true : false}
onChange={handleTextField}
{...params}
InputLabelProps={{ shrink: false }}
label={!text.length && !selected.stateOptions? `Selectd Opitons !!` : ' '}
onBlur={restTextOnBlurCond}
autoComplete='off'
/>
)}
/>

Multiple autocomplete fields with Material UI in same view

I have 2 autocomplete fields on my view by default, but you can add more fields per click.
Currently I have the problem that every autocomplete field opens every result list of every autocomplete field, because every field uses "open". How do I get it to implement the whole thing dynamically?
const [value, setValue] = useState<string>('')
const [open, setOpen] = useState(false)
<Autocomplete
options={props.results.map((option) => option.name)}
renderOption={(option) => (
<Typography noWrap>
{option}
</Typography>
)}
onClose={() => {
setOpen(false)
}}
open={open}
renderInput={(params) => (
<Paper className={search.root} ref={params.InputProps.ref}>
<IconButton className={search.iconButton} disabled>
<FiberManualRecordIcon color="secondary" />
</IconButton>
<InputBase
{...params.inputProps}
className={search.input}
placeholder="Test"
value={value}
onChange={(event: any) => setValue(event.target.value)}
/>
<IconButton
className={search.iconButton}
disabled={!value}
>
<SearchIcon />
</IconButton>
</Paper>
)}
/>
Probably you already solved this due to the date of this issue. But I'm gonna put here what I did to resolve this problem because I was facing the same issue using FieldArray from Formik.
Material UI documentation sometimes is a little confusing, so try to ignore some stuff in their examples.
Remove the open, onOpen, and onClose props. You just need these props if you want to create some kind of automatic opening/closing mechanic.
Here it's an example of what I was doing and how I solved it.
<Autocomplete
id={`items.${index}.partNumber`}
name={`items.${index}.partNumber`}
freeSolo
style={{ width: 300 }}
open={openPartNumber}
onOpen={() => setOpenPartNumber(true)}
onClose={() => setOpenPartNumber(false)}
options={partNumbers}
clearOnBlur={false}
getOptionLabel={option => (option.label ? option.label : '')}
value={item.partNumber}
inputValue={item.partNumber}
onInputChange={(event, newInputValue) => {
if (event) {
setFieldValue(`items.${index}.partNumber`, newInputValue);
getPartNumberList(newInputValue);
}
}}
onChange={(event, optionSelected, reasson) => {
if (reasson === 'select-option') {
setFieldValue(`items.${index}.partNumber`, optionSelected.partNumber);
setFieldValue(`items.${index}.vendorNumber`, optionSelected.vendorNumber);
setFieldValue(`items.${index}.description`, optionSelected.description);
setFieldValue(`items.${index}.ncm`, optionSelected.ncm);
}
}}
filterOptions={x => x}
...otherPrps...
/>
Then I just remove the props open, onOpen and onClose.
<Autocomplete
id={`items.${index}.partNumber`}
name={`items.${index}.partNumber`}
freeSolo
style={{ width: 300 }}
options={partNumbers}
clearOnBlur={false}
getOptionLabel={option => (option.label ? option.label : '')}
value={item.partNumber}
inputValue={item.partNumber}
onInputChange={(event, newInputValue) => {
if (event) {
setFieldValue(`items.${index}.partNumber`, newInputValue);
getPartNumberList(newInputValue);
}
}}
onChange={(event, optionSelected, reasson) => {
if (reasson === 'select-option') {
setFieldValue(`items.${index}.partNumber`, optionSelected.partNumber);
setFieldValue(`items.${index}.vendorNumber`, optionSelected.vendorNumber);
setFieldValue(`items.${index}.description`, optionSelected.description);
setFieldValue(`items.${index}.ncm`, optionSelected.ncm);
}
}}
filterOptions={x => x}
...otherPrps...
/>
If you want automatic opening/closing mechanics to exist. One suggestion is to control open starting from an array.
In the onOpen and onClose props, your callback must control an array by adding and removing the AutoComplete ID and in open just check the existence of this ID inside the array with array.includes(index).
const [value, setValue] = useState<string>('')
//const [open, setOpen] = useState(false)
const [inputsOpen, setInputsOpen] = useState([])
function automatedOpening(id) {
if (id && inputsOpen.length === 0) {
setInputsOpen([...inputsOpen, id]);
}
}
// lenght must be zero to ensure that no other autocompletes are open and will be true on the Open prop
function automatedClosing(id) {
if (id && inputsOpen.length !== 0) {
setInputsOpen(inputsOpen.filter(item => item !== id));
}
}
<Autocomplete
options={props.results.map((option) => option.name)}
renderOption={(option) => (
<Typography noWrap>
{option}
</Typography>
)}
onClose={(e) => automatedClosing(e.id)}
onOpen={(e) => automatedClosing(e.id)}
open={inputsOpen.includs(id)}
renderInput={(params) => (
<Paper className={search.root} ref={params.InputProps.ref}>
<IconButton className={search.iconButton} disabled>
<FiberManualRecordIcon color="secondary" />
</IconButton>
<InputBase
{...params.inputProps}
className={search.input}
placeholder="Test"
value={value}
onChange={(event: any) => setValue(event.target.value)}
/>
<IconButton
className={search.iconButton}
disabled={!value}
>
<SearchIcon />
</IconButton>
</Paper>
)}
/>
you'll probably need to find a way to make the id/index available to the callbacks, but it's open to what you think is best

Material-UI Autocomplete onChange not updates value

I want to use onChange event on Autocomplete component to get current selected values.
The problem is that it does not working as expected, so when I click to check/uncheck value checkbox is still unchecked but in console i can see that new value was added
uncoment this part to make it works:
value={myTempVal}
onChange={(event, newValue) => {
setMyTempVal(newValue);
console.log(newValue);
}}
online demo:
https://codesandbox.io/embed/hardcore-snowflake-7chnc?fontsize=14&hidenavigation=1&theme=dark
code:
const [myTempVal, setMyTempVal] = React.useState([]);
<Autocomplete
open
multiple
value={myTempVal}
onChange={(event, newValue) => {
setMyTempVal(newValue);
console.log(newValue);
}}
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
You need to get donors receivers and options variables out of the function. Those variables get re-created at each render, this means that their reference changes at each render, and as Autocomplete makes a reference equality check to decide if an option is selected he never finds the options selected.
const donors = [...new Set(data.map(row => row.donor))].map(row => {
return {
groupName: "Donors",
type: "donor",
title: row || "null"
};
});
const receivers = [...new Set(data.map(row => row.receiver))].map(row => {
return {
groupName: "Receivers",
type: "receiver",
title: row || "null"
};
});
const option2 = [...donors, ...receivers];
export const App = props => {
const [myTempVal, setMyTempVal] = React.useState([]);
return (
<Autocomplete
open
multiple
...
You can also add getOptionSelected to overwrite the reference check :
<Autocomplete
open
multiple
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
getOptionSelected={(option, value) => option.title === value.title}
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
This can help:
Replace
checked={selected}
To
checked={myTempVal.filter(obj=>obj.title===option.title).length!==0}
The complete solution
import React from "react";
import "./styles.css";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import CheckBoxOutlineBlankIcon from "#material-ui/icons/CheckBoxOutlineBlank";
import CheckBoxIcon from "#material-ui/icons/CheckBox";
import Checkbox from "#material-ui/core/Checkbox";
import SearchIcon from "#material-ui/icons/Search";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
const data = [
{ donor: "Trader Joe's", receiver: "Person-to-Person" },
{ donor: "Trader Joe's", receiver: "Homes with Hope" },
{ donor: "Santa Maria", receiver: "Gillespie Center" },
{ donor: "Santa Maria", receiver: null }
];
export const App = props => {
const donors = [...new Set(data.map(row => row.donor))].map(row => {
return {
groupName: "Donors",
type: "donor",
title: row || "null"
};
});
const receivers = [...new Set(data.map(row => row.receiver))].map(row => {
return {
groupName: "Receivers",
type: "receiver",
title: row || "null"
};
});
const option2 = [...donors, ...receivers];
const [myTempVal, setMyTempVal] = React.useState([]);
return (
<Autocomplete
open
multiple
value={myTempVal}
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No labels"
renderOption={(option, { selected }) => {
return (
<>
<Checkbox
onClick={
()=>{
if(myTempVal.filter(obj=>obj.title===option.title).length!==0){
setMyTempVal([...myTempVal.filter(obj=>obj.title!==option.title)],console.log(myTempVal))
}else{
setMyTempVal([...myTempVal.filter(obj=>obj.title!==option.title),option],console.log(myTempVal))
}
}
}
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={myTempVal.filter(obj=>obj.title===option.title).length!==0}
/>
{option.title}
</>
);
}}
options={option2}
// groupBy={option => option.groupName}
getOptionLabel={option => option.title}
renderInput={params => (
<div>
<div>
<SearchIcon />
</div>
<TextField
variant="outlined"
fullWidth
ref={params.InputProps.ref}
inputProps={params.inputProps}
/>
</div>
)}
/>
);
};
export default App;
It is bit late to Answer this question but it might help someone.
In your code you have added onChange event in Autocomplete. When you click on checkbox it will trigger 2 times, one for checkbox and one for Autocomplte. Hence 2nd time trigger makes again checkbox unchecked so u get value in console but still checkbox is empty.
You can remove your checkbox in renderOption and use checked and uncheked icon instaed of checkbox.
renderOption={(option, { selected }) => {
return (
<React.Fragment>
{selected ? <CheckedIcon> : <uncheckedIcon>}
<div>
{option.title}
</div>
</React.Fragment>
</>
);
}}

Resources