I am trying to implement Auto complete having checkbox.
https://material-ui.com/components/autocomplete/#autocomplete
but when I am implementing same component in final-form I am not able to checked my option why ?
here is my code
https://codesandbox.io/s/relaxed-breeze-hv58o
<Autocomplete
{...rest}
multiple={multiple}
disableCloseOnSelect={true}
options={
multiple
? maxReached
? []
: options.filter(option => !value.includes(option.value))
: options
}
defaultValue={
multiple
? options.filter(option => value.includes(option.value))
: options.find(option => option.value === value)
}
onChange={
multiple
? (_e, values) => {
setMaxReached(value.length >= max - 1);
onChange(values.map(option => option.value));
}
: (_e, option) => onChange(option.value)
}
getOptionLabel={option => option.label}
noOptionsText={
maxReached
? formatMessage({ id: "components.autocomplete.max" }, { max })
: formatMessage({ id: "components.autocomplete.no" })
}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.label}
</React.Fragment>
)}
renderInput={params => (
<TextField
{...params}
{...restInput}
label={label}
placeholder={placeholder || ""}
helperText={
hasError ? formatMessage({ id: error }, { label }) : helperText
}
error={hasError}
fullWidth
/>
)}
/>
);
};
You have some issues with your code (fixed version):
You are calling onChange that makes React-Final-Form re-render, which leads for Autocomplete component to re-render, and remove the select state. To fix this, you will have to use getOptionSelected option.
getOptionSelected={(option, value) => {
return option.value === value.value;
}}
options={
options
}
onChange={(_e, values) => {
onChange(values);
}
You are filtering options based to Autocomplete component, so selected option gets filtered out.
so from this:
options={
multiple
? maxReached
? []
: options.filter(option => !value.includes(option.value))
: options
}
To
options={
options
}
Related
I have a Mui Autocomplete Component with a react-hook-form Controller.
When I set the freeSolo attribute the value doesn't get reset. When I call for example: resetField("selBinvrk") without freeSolo everything works as expected. Here is the code for my autocomplete and the submit function. Can anybody help me with that problem?
<Controller
name={name}
control={control}
rules={rules}
render={({ field: { onChange, value } }) => (
<Autocomplete
disabled={!disabled ? false : true}
value={value}
freeSolo={freeSolo}
//autoSelect={autoSelect}
options={options}
getOptionLabel={getOptionLabel}
isOptionEqualToValue={isOptionEqualToValue}
/* getOptionSelected={(option, value) => {
console.log('get option selected: ', option, value);
return value === undefined || value === "" || option.ID === value.ID}
} */
//console.log(item);
onChange={(event, item) => {
//console.log(item);
onChange(item);
}}
//renderOption={renderOption}
renderInput={(params) => (
<TextField
{...params}
label={label}
variant="outlined"
error={isError}
helperText={errorMessage}
sx={sx}
inputRef={inputRef}
/>
)}
/>
)}
/>
const onSubmit = (data, e) => {
console.log("hä: ", e);
resetField("selBinvrk");
resetField("selMenge");
mutate({
binv_id: data.selBinv.ID,
bsa_id: data.selBinvrk?.BSA_ID || 0,
binvrk_id: data.selBinvrk?.ID || 0,
selMenge: data.selMenge,
bsm_id: data.selBsm.ID,
});
//recaptchaRef.current.execute();
//mutate(data);
};
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.
I'm trying to create a Material-UI Autocomplete component that essentially just displays search results to the user. Some of the options' names will be duplicates, but they will all have unique IDs. I receive the following warning:
index.js:1 Warning: Encountered two children with the same key, Name B. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
const SearchField = () => {
const [open, setOpen] = React.useState(false)
const [searchQuery, setSearchQuery] = React.useState('')
const [searchResults, setSearchResults] = React.useState([])
const loading = true //later
const debounced = useDebouncedCallback(
async searchQuery => {
if (searchQuery) {
let result = await doSearch(searchQuery)
if (result.status === 200) {
setSearchResults(result.data)
} else {
console.error(result)
}
}
},
1000
)
const handleInputChange = e => {
if (e.target.value && e.target.value !== searchQuery) {
debounced(e.target.value)
setSearchQuery(e.target.value)
}
}
const options = [{
name: 'Name A',
id: 'entry_0597856'
},{
name: 'Name B',
id: 'entry_3049854'
},{
name: 'Name B',
id: 'entry_3794654'
},{
name: 'Name C',
id: 'entry_9087345'
}]
return (
<Autocomplete
id='search_freesolo'
freeSolo
selectOnFocus
clearOnBlur
handleHomeEndKeys
autoHighlight
onInputChange={handleInputChange}
open={true}
onOpen={() => setOpen(true)}
onClose={() => setOpen(false)}
loading={loading}
key={option => option.id}
options={options}
getOptionLabel={option => option.name}
renderOption={(props, option) => (
<Box
component='li'
{...props}
>
{option.name}
</Box>
)}
renderInput={params => {
return (
<TextField
{...params}
required
id="search_bar"
label="Search"
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress size={18} /> : null}
{params.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
)}
}
/>
)
}
You can define your own renderOption that can return the list item with a correct key value. Your code complains about the duplicated keys because by default, Autocomplete uses the getOptionLabel(option) to retrieve the key:
<Autocomplete
renderOption={(props, option) => {
return (
<li {...props} key={option.id}>
{option.name}
</li>
);
}}
renderInput={(params) => <TextField {...params} label="Movie" />}
/>
If it still doesn't work, check your props order, you need to declare the key prop last, if you put it before the props provided by the callback:
<Box component='li' key={key} {...props}
Then it will be overridden by the props.key from MUI. It should be like this:
<Box component='li' {...props} key={key}
Live Demo
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.
When working with HTML select in React, we tend to use an id or key to track the value selected:
<select value={value} onChange={(event) => setValue(event.target.value)}>
{options.map((option) => (
<option value={option.id}>{option.label}</option>
))}
</select>
I wonder if we can do the same with Material-ui Autocompelete component since in its demo, the value set in state is the whole object instead of the object id.
I tried using its APIs in the following way which make sense to me but it doesn't work as expected:
const fruits = [
{ id: 0, label: "apple" },
{ id: 1, label: "banana" },
{ id: 2, label: "cherries" },
{ id: 3, label: "fig" }
];
function FruitPicker() {
const [value, setValue] = useState(null);
return (
<Autocomplete
id="fruit-picker"
value={value}
onChange={(event, option) => {
setValue(option?.id || null);
}}
options={fruits}
getOptionLabel={(option) => option.label}
getOptionSelected={(option) => option.id === value}
renderInput={(params) => <TextField {...params} label="Fruit" />}
openOnFocus
/>
);
}
I had created this Codesandbox if you want to play around. Thanks.
This is the method that I used.
<Autocomplete
options={fruits}
value={fruits.filter(el => el.id === currentValue)[0]}
getOptionLabel={option => option.label}
onChange={(event, option) => { setValue(option?.id || null); }}
/>
Because you pass the options of array object, so when set value onChange, you must still keep setValue(option), but on getOptionSelected, compare their ids instead
<Autocomplete
value={value}
onChange={(event, option) => {
setValue(option);
}}
options={fruits}
getOptionLabel={(option) => option.label}
getOptionSelected={(option) => option.id === value.id}
renderInput={(params) => <TextField {...params} label="Fruit" />}
openOnFocus
/>