I am using material-ui Autocomplete and I want to add some default selected options to it.
https://codesandbox.io/s/broken-fire-htxtd?file=/src/App.js
As you can see in the example I am rendering pre-selected items with startAdornment. The problem is when I try to select another item, it automatically deletes the pre-selected items. Also I cannot delete them properly.
The idea is this default selected to stay selected until I remove them. Also to be able to pick another letter without erasing them.
you can use defaultValue property instead of Adornment.
<Autocomplete
multiple
open={open}
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
options={["A", "B", "C", "D", "E"]}
disableCloseOnSelect
defaultValue={cities} //here
onChange={(e, v) => setCities(v)}
getOptionLabel={(option) => option}
renderOption={(option, { selected }) => {
if (cities.includes(option)) {
selected = true;
}
return (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option}
</React.Fragment>
);
}}
renderInput={(params) => {
return (
<TextField
{...params}
variant="outlined"
label="Cities"
placeholder="Enter cities"
autoComplete="off"
/>
);
}}
/>
Related
I use material UI autocomplete to create a single-select dropdown.
But the problem is that when I click the close button placed right-side of the input, the onChange event didn't call and my state didn't update.
While in the multi-select mode this event successfully occurred.
Someone helps me to use the remove event in single-select mode.
This is my single select:
<Autocomplete<Option<T>>
onChange={(e: any, newValue) => {
if (newValue) {
handleChangeValue(newValue.value);
}
}}
sx={{ ...sx }}
id={id}
options={options}
isOptionEqualToValue={(newValue, option) =>
newValue.value === option.value
}
defaultValue={
defaultValue && {
value: defaultValue,
label: options.find((option) => option.value === defaultValue)?.label,
}
}
value={
value && {
value: value,
label: options.find((option) => option.value === value)?.label,
}
}
getOptionLabel={(option) => option.label || `${option.value}`}
renderOption={(props, option, { selected }) => (
<li value={option.value} {...props}>
<Checkbox
style={{ marginRight: 8 }}
checked={selected}
id={`${option.value}`}
/>
{option.label || `${option.value}`}
</li>
)}
renderInput={(params) => (
<TextField
value={value}
{...params}
placeholder={global.translate(placeholder)}
/>
)}
/>
this one is my multi-select autocomplete:
<Autocomplete
onChange={(e: any, value) => {
onChange(value);
}}
value={value}
sx={{ ...sx }}
multiple
id={id}
options={options}
disableCloseOnSelect
defaultValue={[...defaultValues]}
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(newValue, option) =>
newValue.value === option.value
}
renderOption={(props, option, { selected }) => (
<li value={option.value} {...props}>
<Checkbox
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
checkedIcon={<CheckBoxIcon fontSize="small" />}
style={{ marginRight: 8 }}
checked={selected}
id={option.value}
/>
{option.label}
</li>
)}
renderInput={(params) => (
<TextField {...params} placeholder={placeholder} />
)}
/>
In your single-select mode Autocomplete component, you send handleChangeValue if only newValue exists.
So, in your single-select mode, you need to change this code:
<Autocomplete<Option<T>>
onChange={(e: any, newValue) => {
if (newValue) {
handleChangeValue(newValue.value);
}
}}
to this code:
<Autocomplete<Option<T>>
onChange={(e: any, newValue) => {
const valueToBeSent = newValue ? newValue.value : undefined;
handleChangeValue(valueToBeSent);
}}
in order to send onChange event on every value change.
[
{ label: 'First', checked: false },
{ label: 'Second', checked: true }
]
Here is a very short snippet of how the data could look like.
I am using Material UI's Autocomplete, to make it possible for searching on labels.
These labels are having a checkbox.
Problem is, I can't use onChange on <Checkbox /> when its a renderOption
The Autocomplete just closes, without doing any action
<Autocomplete
disableCloseOnSelect={true}
options={array}
getOptionLabel={option => option.label.toString()}
renderInput={params => (
<TextField
{...params}
fullWidth
label="Select label"
variant="outlined"
error={false}
/>
)}
renderOption={opt => (
<div>
<Checkbox
checked={opt.checked}
onChange={() => alert('not being fired...')}
/>
<p>{opt.label}</p>
</div>
)}
/>
You shouldn't use onChange in Checkbox, You should use onChange prop of Autocomplete, and set default value by value prop
and Checkbox receive its props from renderOption's props
const [selectedOptions, setSelectedOptions] = useState([]);
useEffect(() => {
setSelectedOptions(options.filter(op => op.checked));
}, []);
<Autocomplete
options={options}
value={selectedOptions}
onChange={(event, newValue) => {
setSelectedUsers(newValue);
}
renderOption={(props, option, { selected }) => (
<div {...props}>
<Checkbox checked={selected} />
<p>{option.name}</p>
</div>
)}
disableCloseOnSelect
/>
onChange event will work in Checkbox provided in renderOption function but it only works when you click on checkbox not anywhere else on list item. You can use onClick on the parent div if that fulfills your purpose.
MATERUAL UI Autocomplete component works fine, but I want to get object.id as onSelect event value (event.target.value), not the object.name. In other words, I want to display object.name as select item labels, but I want to get object.id as onSelect event value (event.target.value). Right now, my event.target.value is the same as select item label (object.name). Here is an example (from the Material UI documentation):
The options object is like this:
const options = [
{ id: "01", name: "Peter" },
{ id: "02", name: "Mary },
{ id: "03", name: "John" }
]
And the Autocomplete is the same like in Material UI documentation:
<Autocomplete
id="asynchronous-demo"
fullWidth
open={open}
onOpen={() => {
setOpen(true)
}}
onClose={() => {
setOpen(false)
}}
getOptionLabel={(option) => option.name}
options={options}
loading={loading}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
onChange={(event) => {
if (event.target.value !== '' || event.target.value !== null) {
onChangeHandle(event.target.value)
}
}}
onSelect={(event) => {
onSelectHandle(event)
}}
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? (
<CircularProgress color="inherit" size={20} />
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
With onSelect I always get object.name as event.target.value, but I want to return object.id as event.target.value.
Does anybody knows how??
You are currently grabbing the value from TextField's onSelect, instead of Autocomplete's onChange.
<Autocomplete
...
onChange={(event, newValue) => {
onSelectHandle(newValue)
}}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
onChange={(event) => {
if (event.target.value !== '' || event.target.value !== null) {
onChangeHandle(event.target.value)
}
}}
...
/>
)}
/>
For more info, check out the controllable states section in the Autocomplete documentation.
I want to implement two buttons Select All and Select None inside Autocomplete React Material UI along with checkbox for each option.When Select All button is clicked all the options must be checked and when I click Select None all the options must be unchecked.
How do I implement that ?
<Autocomplete
id={id }
size={size}
multiple={multiple}
value={value}
disabled={disabled}
options={items}
onChange={handleChange}
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment >
{isCheckBox(check, selected)}
{option.label}
</React.Fragment>
)}
renderInput={params => (
<TextField id="dropdown_input"
{...params} label="controlled" variant={variant} label={label} placeholder={placeholder} />
)}
/>
export function isCheckBox(check, selected) {
if (check) {
const CheckBox = <Checkbox
id="dropdown_check"
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
return CheckBox;
}
return null;
}
I stumbled into the same issue earlier today.
The trick is to use local state to manage what has been selected, and change the renderOption to select * checkboxes if the local state has the 'all' key in it.
NB: At the time of writing React 16 is what I'm working with
I'm on a deadline, so I'll leave a codesandbox solution for you instead of a rushed explanation. Hope it helps :
Select All AutoComplete Sandbox
Updated
for React version 16.13.1 and later. codesandbox
const [open, setOpen] = useState(false);
const timer = useRef(-1);
const setOpenByTimer = (isOpen) => {
clearTimeout(timer.current);
timer.current = window.setTimeout(() => {
setOpen(isOpen);
}, 200);
}
const MyPopper = function (props) {
const addAllClick = (e) => {
clearTimeout(timer.current);
console.log('Add All');
}
const clearClick = (e) => {
clearTimeout(timer.current);
console.log('Clear');
}
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
return (
<Autocomplete
PopperComponent={MyPopper}
onOpen={(e) => {
console.log('onOpen');
setOpenByTimer(true);
}}
onClose={(obj,reason) => {
console.log('onClose', reason);
setOpenByTimer(false);
}}
open={open}
.....
....
/>
);
Old Answer
Just customise PopperComponent and do whatever you want.
Autocomplete API
const addAllClick = (e: any) => {
setValue(items);
};
const clearClick = (e: any) => {
setValue([]);
};
const MyPopper = function (props: any) {
return (
<Popper {...props}>
<ButtonGroup color="primary" aria-label="outlined primary button group">
<Button color="primary" onClick={addAllClick}>
Add All
</Button>
<Button color="primary" onClick={clearClick}>
Clear
</Button>
</ButtonGroup>
{props.children}
</Popper>
);
};
<Autocomplete
PopperComponent={MyPopper}
...
/>
If you want to make an autocomplete with select all option using react material ui and react hook form, you can implement to Autocomplete like so
multiple: To allow multiple selection
disableCloseOnSelect: To disable the close of the box after each selection
options: Array of items of selection
value: Selected options.
getOptionLabel: The string value of the option in our case is name
filterOptions: A function that determines the filtered options to be rendered on search, in our case we used it to add selectAll checkbox.
renderOption: Render the option, use getOptionLabel by default.
renderInput: To render the input,
onChange: Callback fired when the value changes
Now you can play with selected values using handleChange so once the select is fired check if the selected option is select all if yes then set the newest selectedOptions
<Autocomplete
multiple
disableCloseOnSelect
options={items}
value={selectedOptions}
getOptionLabel={(option) => option.name}
filterOptions={(options, params) => {
const filtered = filter(options, params)
return [{ id: 0, name: selectAllLabel }, ...filtered]
}}
renderOption={(props, option, { selected }) => {
// To control the state of 'select-all' checkbox
const selectAllProps =
option.name === 'Sélectionner Tous' ? { checked: allSelected } : {}
return (
<li {...props}>
<Checkbox checked={selected} {...selectAllProps} />
{option.name}
</li>
)
}}
renderInput={(params) => (
<TextField {...params} label={label} placeholder={label} />
)}
onChange={handleChange}
/>
you can refer to the Autocomplete API to get detailed definition of each item
You can refer to this codeSendBox to check a demo of react material Autocomplete with select all using react material ui version 5 and react hook form verion 7
Could you please tell why my required check is not working in autocomplete .I am using material UI with react hook form.
Step to reproduce
Click Submit button it show field is required.
then select any element from list.
remove the selected element Then click again submit button.It should
show “required” field check.but it is not showing anything why ??
Here is my code
https://codesandbox.io/s/mui-autocomplete-with-react-hook-form-0wvpq
<Controller
as={
<Autocomplete
id="country-select-demo"
multiple
style={{ width: 300 }}
options={countries}
classes={{
option: classes.option
}}
autoHighlight
getOptionLabel={option => option.label}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.label} ({option.code}) +{option.phone}
</React.Fragment>
)}
renderInput={params => (
<TextField
{...params}
label="Choose a country"
variant="outlined"
fullWidth
name="country"
inputRef={register({ required: true })}
// required
error={errors["country"] ? true : false}
inputProps={{
...params.inputProps,
autoComplete: "disabled" // disable autocomplete and autofill
}}
/>
)}
/>
}
onChange={([event, data]) => {
return data;
}}
name="country"
control={control}
/>
When the form loads initially, the value of your form is an empty object -
{}
When you select a country (say, 'Andorra') the value of your form becomes:
{"country":[{"code":"AD","label":"Andorra","phone":"376"}]}
And then when you deselect the country, the value of your form becomes:
{"country":[]}
An empty array technically meets the "required" criteria (it's not null, after all) so your required handler doesn't fire.
You can verify this is happening by showing the value of the form in your App class -
const { control, handleSubmit, errors, register, getValues } = useForm({});
return (
<form noValidate onSubmit={handleSubmit(data => console.log(data))}>
<Countries control={control} errors={errors} register={register} />
<Button variant="contained" color="primary" type="submit">
Submit
</Button>
<code>{JSON.stringify(getValues())}</code>
</form>
);
The simple fix is to NOT return an empty array as a value from your control - update your onChange handler as follows -
onChange={([event, data]) => {
return data && data.length ? data : undefined;
}}