I have a Material UI autocomplete component with a checkbox component. How can I get both of them working such that only when a user selects an option from the autocomplete, the checkbox should get checked. Here is the link to my component:
https://codesandbox.io/embed/material-demo-forked-of2cz?codemirror=1
https://codesandbox.io/s/material-demo-forked-of2cz?from-embed=&file=/demo.tsx
i finally got my autocomplete/checkbox to work and be all checked by default (redux & ts)
const HIDDEN_USERS: HiddenUsersProps[] = store.getState().user.hiddenUsers
// array of objects
//{
// uid:"oKW8wDsasYdssdd1wTLggsas02"
// userName:"Matt"
//}
const [value, setValue] = useState<HiddenUsersProps[]>([])
//the only way i found to wait for the values from the redux store
useEffect(() => {
setValue(HIDDEN_USERS)
}, [HIDDEN_USERS])
const handleChange = (newValue: HiddenUsersProps[]) => {
setValue(newValue)
}
return (
<Autocomplete
options={HIDDEN_USERS}
value={value}
onChange={(_, newValue: HiddenUsersProps[]) => handleChange(newValue)}
getOptionLabel={(option: any) => option.userName}
isOptionEqualToValue={(option, value) => option.userName === value.userName}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox icon={icon}
checkedIcon={checkedIcon}
checked={selected} />
{option.userName}
</li>
)}
renderInput={(params) => (
<TextField {...params}
label="Hidden Users"
placeholder="Name"
variant="outlined" />)} />
)
"#mui/material": "^5.0.6",
You should change your component like this :
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import Checkbox from "#material-ui/core/Checkbox";
const options = ["Option 1", "Option 2"];
export default function ControllableStates() {
const [value, setValue] = React.useState<string | null>("");
const [inputValue, setInputValue] = React.useState("");
const [checked, setChecked] = React.useState<boolean>(false);
const [text1, setText1] = React.useState("");
const [text2, setText2] = React.useState("");
const isTextFieldsNotEmpty = text1.length > 0;
const handleFirstTextChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setChecked(!checked)
setText1(event.target.value);
};
return (
<div>
<div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
<div>{`inputValue: '${inputValue}' '${checked}' '${text1}'`}</div>
<br />
<Checkbox
checked={checked}
onChange={handleFirstTextChange}
inputProps={{ "aria-label": "primary checkbox" }}
/>
<Autocomplete
value={value}
onChange={(event: any, newValue: string | null) => {
setValue(newValue);
setChecked(!checked)
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
options={options}
style={{ width: 300 }}
renderInput={(params) => (
<TextField
{...params}
onChange={handleFirstTextChange}
label="Controllable"
variant="outlined"
/>
)}
/>
</div>
);
}
I just have put the setChecked inside the same event listener than your textfield.
That would create the behaviors you search when the user select a field is checking the checkbox.
Related
I create a component for my Dialog and my Checkbox my issue is when my checkbox is not in the Dialog the update works but when it's inside it doesn't work. I don't understand why.
const Popup = ({ title, handleClose, openned, children }) => {
return (
<Dialog className='react-popup-template' fullWidth={true} maxWidth='sm' open={openned} onClose={handleClose} aria-labelledby="parent-modal-title" aria-describedby="parent-modal-description">
<DialogContent id="modal-description" >
<div>
{title && <div><h4 style={{ textAlign: 'center', fontWeight: 'bold', fontSize : '23px' }}>{title}</h4><br/></div>}
{children}
</div>
</DialogContent>
</Dialog>
);
}
const CheckBox = (value, onChange) => {
return (
<label>
<input type='checkbox' value={value} onChange={onChange} />
</label>)
}
const App = () =>{
const [openPopup, setOpenPopup] = React.useState(false)
const [checked, setChecked] = React.useState(false)
const [title, setTitle] = React.useState('')
const [description, setDescription] = React.useState('')
const showModal = (title) =>{
setTitle(title)
setDescription(<CheckBox value={checked} onChange={() => {setChecked(!checked)}} />)
}
return (
<button onClick={() => {showModal('Title')}}>showModal</button>
<PopupTemplate title={title} handleClose={() => { setOpenPopup(false) }} openned={openPopup}>
{description}
</PopupTemplate>)
}
In your Checkbox you should either destructure your props
const CheckBox = ({ value, onChange }) => {
return (
<label>
<input type="checkbox" value={value} onChange={onChange} />
</label>
);
};
Or use your props via the props value
const CheckBox = (props) => {
return (
<label>
<input type="checkbox" value={props.value} onChange={props.onChange} />
</label>
);
};
EDIT:
The state only updates the first time you click the checkbox. Using the callback in the setChecked method will solve this.
...
setDescription(
<CheckBox
value={checked}
onChange={() => {
setChecked((prevChecked) => !prevChecked);
}}
/>
);
...
PS: I don't now if its just a copy/paste error, but you're missing setOpenPopup(true) in your showModal function.
Try this, as mui uses forwarRef for its components this should work,
setDescription(<CheckBox checked={checked} onChange={e => setChecked(!checked)} />)
The autocomplete looks like this
<Autocomplete options={speceificLocation.locationOptions} onChange = {(event,value) => (speceificLocation.locationOptions.includes(value)) ? dispatch({allCities:state.allCities, mappedCities:true}):dispatch({allCities:state.allCities, mappedCities:false})} renderInput = {(params) => <TextField {...params} label = 'Cities'/>}/>
Here is a live demo of an Autocomplete that can be cleared by a button.
import * as React from "react";
import Button from "#mui/material/Button";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
const options = ["Option 1", "Option 2"];
export default function ControllableStates() {
const [value, setValue] = React.useState<string | null>(options[0]);
const [inputValue, setInputValue] = React.useState("");
return (
<div>
<div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
<div>{`inputValue: '${inputValue}'`}</div>
<br />
<Button onClick={() => setValue("")}>Clear</Button>
<br />
<br />
<Autocomplete
value={value}
onChange={(event: any, newValue: string | null) => {
setValue(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
options={options}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Controllable" />}
/>
</div>
);
}
I am trying to write code to asynchronously search a multiple-select combo upon keyboard entry.
However I found in latest version (5.2.2) a strange behaviour where I cannot explain. I distill the issue below (based on example from MUI's autocomplete page):
import * as React from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
const options = [
{ label: "Option 1", value: 1 },
{ label: "Option 2", value: 2 }
];
export default function ControllableStates() {
// const [value, setValue] = React.useState<any | null>([]);
const value = [];
const [inputValue, setInputValue] = React.useState("");
console.log("Current Value:", value);
return (
<div>
<div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
<div>{`inputValue: '${inputValue}'`}</div>
<br />
<Autocomplete
multiple={true}
value={value}
onChange={(event: any, newValue: any | null) => {
//setValue(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
options={options}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Controllable" />}
/>
</div>
);
}
The codeSandbox is as follows: https://codesandbox.io/s/controllablestates-material-demo-forked-ygqp2?file=/demo.tsx
If you try in the codeSandbox, you will be unable to type anything in the TextField field.
However, if you switch the commenting:
const [value, setValue] = React.useState<any | null>([]);
// const value = [];
You will be able to type in the TextField field. What is actually happening here? The value did not change at all.
Can anyone figure out why my first code (where the value is a const empty array) didn't work?
The reason I am asking is that I need to pass in the (controlled) value as props, and then set it to default to [] if it is null. I find that I am unable to type in the TextField due to this defaulting.
First, you could use the Autocomplete component without inputValue and OnInputValue props.
...
<Autocomplete
multiple
value={value}
onChange={(event: any, newValue: any | null) => {
//setValue(newValue);
}}
id="controllable-states-demo"
options={options}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Controllable" />}
/>
But it won't work for the selection, only search will work.
Second, if you want its search as well as selection to work, then you should use more a couple of Autocomplete props.
...
export default function ControllableStates() {
const [value, setValue] = React.useState<any | null>([]);
// you need to set the selected value your own
// const value = [];
const [inputValue, setInputValue] = React.useState("");
console.log("Current Value:", value);
return (
<div>
<div>{`value: ${value !== null ? `'${value}'` : "null"}`}</div>
<div>{`inputValue: '${inputValue}'`}</div>
<br />
<Autocomplete
multiple
value={value}
onChange={(event: any, newValue: any | null) => {
setValue(newValue.map(option => option.value || option));
}}
isOptionEqualToValue={(option, value) => option.value === value}
getOptionLabel={(option) => {
if (typeof option === 'number') {
return options.find(item => item.value === option)?.label;
} else {
return option.label;
}
}}
id="controllable-states-demo"
options={options}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Controllable" />}
/>
</div>
);
}
As you can see it doesn't need to use the inputValue and onInputChange props as well.
Please make sure if you match the correct types of the selected value and option.
If you are using react-hook-form you can set up the autocomplete by using
multiple to add multiple values,
options: you add the options to be selected
getOptionLabel: to show up the label of the options
onChange: use onChange function of react-hook-form to set the selected values
renderInput: to render the input
import { useForm, Controller } from 'react-hook-form'
import {
Box,
TextField,
Autocomplete,
} from '#mui/material'
const {
...
control,
formState: { errors },
} = useForm()
<Box mt={2}>
<Controller
control={control}
name="industries"
rules={{
required: 'Veuillez choisir une réponse',
}}
render={({ field: { onChange } }) => (
<Autocomplete
defaultValue={
useCasesData?.industries
? JSON.parse(useCasesData?.industries)
: []
}
multiple
disableCloseOnSelect
options={companyIndustryTypes}
getOptionLabel={(option) => option.name}
onChange={(event, values) => {
onChange(values)
}}
renderInput={(params) => (
<TextField
{...params}
label="Type d'industries"
placeholder="Type d'industries"
helperText={errors.industries?.message}
error={!!errors.industries}
/>
)}
/>
)}
/>
</Box>
Note that options in my case companyIndustryTypes is an array of object :
[
{
id: 1,
name: "Accounting",
},
{
id: 2,
name: "Administration & Office Support",
},
...
]
I have an Autocomplete component and I need to prevent it for selecting some kind of values.
const options = ["Option 1", "Option 2", "Option 3"];
const [value, setValue] = React.useState(options[0]);
const [inputValue, setInputValue] = React.useState("");
const change = (event, newValue) => {
event.preventDefault();
event.stopPropagation();
if (newValue !== "Option 3") {
setValue(newValue);
}
};
console.log(value);
return (
<div>
<Autocomplete
value={value}
onChange={change}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
disableClearable
options={options}
style={{ width: 300 }}
renderInput={(params) => (
<TextField {...params} label="Controllable" variant="outlined" />
)}
/>
</div>
);
Currently the condition does not change the state, but it does change the value on the component. How can I prevent this to happen?
Here is the sandbox.
You are setting value again here, dont update on this event
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
set inputValue={value}
Updated here,
https://codesandbox.io/s/material-demo-forked-jmr6e?file=/demo.js
In the hooks version of material UI I can't seem to be able to clear the autocomplete after an onChange event:
// #flow
import React, { useRef, useState } from "react";
import "./Autocomplete.scss";
import AutocompleteUI from "#material-ui/lab/Autocomplete";
import TextField from "#material-ui/core/TextField";
function Autocomplete(props) {
const { options } = props;
const [value, setValue] = useState();
const container = useRef();
const input = useRef();
function onChange(event, newValue) {
if (!newValue) return;
props.onChange(newValue);
setValue(undefined);
input.current.value = "";
event.target.value = "";
}
function renderInput(params) {
return (
<TextField
inputRef={input}
{...params}
inputProps={{
...params.inputProps,
autoComplete: "disabled", // disable autocomplete and autofill
}}
margin="none"
fullWidth
/>
);
}
return (
<div className="Autocomplete-container">
{value}
<AutocompleteUI
ref={container}
options={options}
autoHightlight={true}
clearOnEscape={true}
autoSelect={true}
// freeSolo={true}
getOptionLabel={option => option.title}
renderInput={renderInput}
value={value}
onChange={onChange}
/>
</div>
);
}
export default Autocomplete;
Diving into the source code I've noticed the component uses useAutocomplete hook internally. However, neither setInputValue nor resetInputValue which live internally inside that hook are exposed outside. Is there a way to accomplish an input clear after an onChange?
You need to set the inputValue prop to your valueState and on onhange function just clear the valueState
<Autocomplete
inputValue={valueState}
onChange={(value, option) =>
{
setOptions([])
setValueState("")
}}
renderInput={params => (
<TextField
dir="rtl"
onChange={(event) =>
{
setValueState(event.target.value)
}}
{...params}
label="Search Patient"
variant="filled"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
)}
/>
I had the same issue and I solved it with this :
const [search, setSearch] = useState("");
...
<Autocomplete
id="grouped-demo"
options={tagsList}
getOptionLabel={(option) => option.tag}
onChange={(event, value) =>value ? setSearch(value.tag) : setSearch(event.target.value)}
style={{width: 700}}
renderInput={(params) => <TextField {...params} label="Search" variant="outlined"/>}
/>
I encountered a similar scenario using Autocomplete/Textfield in a Search/Nav bar. The value would always be left behind after using a history.push or other Router function in the onChange event. The trick is to set the inputValue = "" . Every time the component renders, the previous value will be removed. See below
<Autocomplete
{...defaultProps}
onChange={(event, value) => {
if(value)
router.history.push(`/summary`);
}}
filterOptions={filterOptions}
clearOnEscape={true}
inputValue=""
renderInput={params => <TextField {...params}
label="Quick Search" fullWidth
InputProps={{...params.InputProps,
'aria-label': 'description',
disableUnderline: true,
}}/>}
/>
yo! I'm pretty sure the Textfield component from material takes an "autoComplete" prop, and you can pass that the string "false". Also, it does not go in inputProps, try that out.
<Textfield autoComplete="false" />
I had the same issue and I solved it with this :
const [value, setValue] = useState(null);
Then you don't need to use refs.