How to disable the selected option in the list of options in autocomplete MUI?
For example, after selecting option "b", it should be disabled.
import Autocomplete from "#material-ui/lab/Autocomplete";
import { TextField } from "#material-ui/core";
function App() {
return (
<Autocomplete
freeSolo
id="free-solo-demo"
options={["a", "b", "c"]}
renderInput={params => (
<TextField
{...params}
label="freeSolo"
margin="normal"
variant="outlined"
fullWidth
/>
)}
/>
);
}
You can achieve this using getOptionDisabled prop. You just have to pass a function to this prop which says to disable the option if already been selected.
There is another prop available in this component called filterSelectedOptions, which filters out the selected option.
You can find both of these working examples over here Stackblitz👈
const [selectedOptions, setSelectedOptions] = useState([]);
const options = ['a', 'b', 'c'];
<Autocomplete
multiple
freeSolo
options={options}
value={selectedOptions}
//disabling selected options
getOptionDisabled={(option) =>
selectedOptions.some((selectedOption) => selectedOption === option)
}
onChange={(_, value) => {
setSelectedOptions(value);
}}
renderInput={(params) => (
<TextField
{...params}
label="Multiple values"
placeholder="Favorites"
/>
)}
/>
I have a form in Laravel 6 that uses a checkbox to hide/show additional fields. So the fields are hidden, but when the user checks the box ('attorney') and the fields appear, they are required. Otherwise, when they are hidden (checkbox unchecked) they are not required.
The following are the validation rules for the form:
public function rules()
{
return [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required',
'role' => 'required',
'role.*' => 'exists:roles,id',
'attorney' => 'nullable',
'bar_number' => 'nullable|required_if:attorney,1',
'law_firm_email' => 'nullable|required_if:attorney,1',
'law_firm_address' => 'nullable|required_if:attorney,1',
'law_firm_name' => 'nullable|required_if:attorney,1',
];
}
This hide/unhide works fine for users who check the box, and the form works for those that submit that extra info, but for those that don't check the box (those extra fields remain hidden) the form fails with the error "The bar number must be a string" (bar number is first field in list). The hidden fields are still being validated even though the checkbox is unchecked (the condition required_if should be false). I have tried 'checked' and 'on' for the checkbox value, same result.
This is the checkbox in the view:
<div class="col-md-6"></br>
<input id="attorney" type="checkbox" name="attorney" value="{{ old('attorney') }}"> Check if registering a law firm
#if ($errors->has('attorney'))
<span class="help-block">
<strong>{{ $errors->first('attorney') }}</strong>
</span>
#endif
</div>
This is the JavaScript that hides/shoes the DIV with the additional fields:
<script>
$(document).ready(function() {
$('input[name="attorney"]').change(function() {
if(this.checked) {
$('#bar_number_div').fadeIn('fast');
} else {
$('#bar_number_div').fadeOut('fast');
}
});
});
</script>
The JavaScript works fine as expected. The validation config is my issue.
There are no errors in the console.
I thought the required_if condition in the validation rules should work as expected, i.e., when the checkbox "attorney" is unchecked the required validation rule is not applied.
You can try with on in required_if
public function rules() {
return [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required',
'role' => 'required',
'role.*' => 'exists:roles,id',
'attorney' => 'nullable',
'bar_number' => 'nullable|required_if:attorney,on',
'bar_number' => 'nullable|required_if:attorney,on',
'law_firm_email' => 'nullable|required_if:attorney,on',
'law_firm_address' => 'nullable|required_if:attorney,on',
'law_firm_name' => 'nullable|required_if:attorney,on',
];
}
Remove value from input checkbox
<input id="attorney" type="checkbox" name="attorney">
I am using a material ui autocomplete field in a Formik form, the form is part of a step form and my problem is that I cannot retrieve the value of the autocomplete field that has been selected by the user when I go back and forth through the form steps . Next I expose the piece of code that I have implemented and that has such a reported problem. Thank you all for the help you can provide.
<Autocomplete
defaultValue= {obrasoc.find((os) => os.nom_os == field.value)}
options={obrasoc}
getOptionLabel={(option) => option.nom_os}
isOptionEqualToValue={(option, value) => option.nom_os === value.nom_os}
renderInput={(params) => (
<TextField
{...params}
label='Obra Social'
variant="standard"
error={meta.touched && meta.error && true}
helperText={_renderHelperText()}
/>
)}
onChange={(e, value) => {
console.log('onchange', value);
fieldHelpers.setValue(value.nom_os)
}}
/>
Finally I found the issue, only i had to replace the line label='Obra Social' with this label= {field.value ? field.value : 'Obra Social'} and now i can surf over differents steps of form and the field in question remember the user input.
I am using material ui V5,
Due to the default filtering in Autocomplete does not give proper result array, l have written my own filterOptions function.
const filterOpt = (options, state) => {
let result = options.filter(option => option.name.includes(state.inputValue))
return result }
The result returning from the function is exactly what l want. But still, l can see the undesired options.
Here is my Autocomplete component :
<StyledAutocomplete
disabled={disabled}
id="field1"
getOptionLabel={(option) => option.name || ""}
isOptionEqualToValue={(option, value) => option.id === value.id}
value={values[prop] || ""}
noOptionsText={"No options found"}
options={data}
style={{ width: "100%" }}
PopperComponent={PopperMy}
PaperComponent={CustomPaper}
onChange={(event, newValue) =>
setValues({ ...values, [prop]: newValue })
}
filterOptions={(options, state) => filterOpt(options, state)}
renderInput={(params) => {
const inputProps = params.inputProps;
inputProps.autoComplete = "new-password";
return (
<StyledTextField
{...params}
inputProps={{
...params.inputProps,
autoComplete: "new-password",
}}
name="field1"
id="field1"
autoComplete="off"
type="text"
label=""
variant="outlined"
error={error && !values[prop]}
helperText={error && errorStatus ? errorTexts[prop] : ""}
/>
);
}}
/>
Here are the options that l see after filtering
Here is the results array returned from the function:
Is there any solution to show the exact filtered array to users?
This is because of my data array which includes some items with the same key.
Does anyone know how to add a default value to the Autocomplete component?
The component have a dataSource, and I'd like to load the page with a specific item already selected(e.g. fill the text field with the selected item's text and it's value already set).
Does anyone knows how? Big thanks for any help <3
You can achieve this by setting the appropriate state in componentDidMount, for example:
componentDidMount() {
// load your items into your autocomplete
// set your default selected item
this.setState({ allItems: [itemYouWantToSet], selectedItem: item.name, selectedItemId: item.id }
}
render() {
return (
<Autocomplete
value={this.state.selectedItem}
items={this.state.allItems}
getItemValue={(item) => item.name}
onSelect={(value, item) => {
this.setState({ selectedItem: value, selectedItemId: value, allItems: [item] });
}}
/>
)
}
Then your item is correctly selected from the list of available options when it loads.
I tried all the above solutions and nothing worked. Perhaps the API has changed since then.
I finally figured out a solution. It's not so elegant, but in principle it makes sense. In my case the options are objects. I just had to set the "value" prop using the exact item from my options array. This way componentDidMount and getOptionSelected aren't needed.
Autocomplete is wrapped inside another component in our case. This is the main code:
class CTAutoComplete extends React.PureComponent {
getSelectedItem(){
const item = this.props.options.find((opt)=>{
if (opt.value == this.props.selectedValue)
return opt;
})
return item || {};
}
render() {
return (
<Autocomplete
id={this.props.id}
className={this.props.className}
style={{ width: '100%' }}
options={this.props.options}
getOptionLabel={this.props.getOptionLabel}
renderInput={params => (
<TextField {...params} label={this.props.label} variant="outlined" />
)}
onChange={this.props.onChange}
value={this.getSelectedItem()}
/>
);
}
}
IMPORTANT: When setting "value", you have to make sure to put the null case " || {} ", otherwise React complains you are changing from an uncontrolled to controlled component.
you can provide the defaultValue prop for AutoComplete.
<Autocomplete
multiple
id="tags-outlined"
options={this.state.categories}
getOptionLabel={(option) => option.category_name}
onChange={this.handleAutocomplete}
defaultValue={'yourDefaultStringValue'} //put your default value here. It should be an object of the categories array.
filterSelectedOptions
renderInput={(params) => (
<TextField
fullWidth
{...params}
variant="outlined"
label="Add Categories"
placeholder="Category"
required
/>
}
/>
This approach works for me (using hooks):
First of all define the options you need in a variable:
const genderOptions = [{ label: 'M' }, { label: 'V' }];
Second you can define a hook to store the selected value (for example store it in session storage for when the page refreshes, or use useState directly):
const age = useSessionStorage('age', '');
Next you can define your Autocomplete as follows (notice the default values in value and getOptionLabel, if you omit those you'll get those controlled to uncontrolled warnings):
<Autocomplete
id="id"
options={ageOptions}
getOptionLabel={option => option.label || ""}
value={ageOptions.find(v => v.label === age[0]) || {}} // since we have objects in our options array, this needs to be a object as well
onInputChange={(_, v) => age[1](v)}
renderInput={params => (
<TextField {...params} label="Leeftijd" variant="outlined" />
)}
/>
It is tricky specially in case of you are using along with filter option which load API on every filter. I was able to load initial value by setting up within state and onInputChange option.
Below is code that work for me or click below link for full working demo.
https://codesandbox.io/s/smoosh-brook-xgpkq?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
import TextField from "#material-ui/core/TextField";
import Typography from "#material-ui/core/Typography";
import Autocomplete from "#material-ui/lab/Autocomplete";
export default function CreateEditStrategy({ match }) {
const [user, setUser] = useState({
_id: "32778",
name: "Magic User's Club!"
});
const [filter, setFilter] = useState("");
const [users, setUsers] = useState([]);
const [openAutoComplete, setOpenAutoComplete] = React.useState(false);
useEffect(() => {
(async () => {
//Will not filter anything for testing purpose
const response = await fetch(
`https://api.tvmaze.com/search/shows?q=${filter}`
);
const shows = await response.json();
setUsers(
shows.map((a, i) => {
return { _id: a.show.id, name: a.show.name };
})
);
})();
}, [filter]);
return (
<div>
<Typography variant="h6">Autocomplete</Typography>
<Autocomplete
open={openAutoComplete}
onOpen={() => setOpenAutoComplete(true)}
value={user}
inputValue={filter}
onClose={() => setOpenAutoComplete(false)}
onChange={(event, user) => {
if (user) setUser(user);
else setUser({ _id: "", name: "" });
}}
onInputChange={(event, newInputValue) => setFilter(newInputValue)}
getOptionSelected={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={users}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
InputProps={{
...params.InputProps
}}
/>
)}
/>
</div>
);
}
Call your component like this
<SelectCountryAutosuggest searchText="My Default Value" />
Make sure you apply the default value to state on class load
class SelectCountryAutosuggest extends React.Component {
state = {
value: this.props.searchText, //apply default value instead of ''
suggestions: [],
};
...
}
The api docs suggest the best approach in the current version (June 2022) is to use value and isOptionEqualToValue.
So for example, if I have a list of users and am choosing which user this thing is assigned to:
const [assignedTo, setAssignedTo] = useState(initialOption);
return (<Autocomplete
options={users.map((i) => ({
label: i.name,
value: i._id,
}))}
isOptionEqualToValue={(o, v) => o.value === v.id}
value={assignedTo}
onChange={(evt, val) => setAssignedTo(val)}
renderInput={(params) => (
<TextField {...params} label="Assigned To" />
)}
/>);
We can setup initial value through value property of Autocomplete component
<Autocomplete
fullWidth={true}
label={'Location'}
margin={'noraml'}
multiple={false}
name={'location'}
getOptionSelected={useCallback((option, value) => option.value === value.value)}
value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
options={location}
ref={locationRef}
onChange={useCallback((e, v) => handleInputChange(e, v))}
/>
You can use the searchText property.
<AutoComplete searchText="example" ... />
Try this...
componentWillReceiveProps(nextProps) {
let value = nextProps.value
if (value) {
this.setState({
value: value
})
}
}
onUpdateInput worked for me - for anyone looking through all this as I was
Have you tried setting the searchText prop dynamically? You can pass the value you want to set to the Autocomplete component as the searchText prop. Something like,
<Autocomplete
searchText={this.state.input.name} // usually value={this.state.input.name}
/>
By default, it'll have the initial value set into the TextField of the Autocomplete component but when the user makes any modifications, it calls up the Autocomplete options depending on the dataSource prop.