'How Can i limit the maximum number of options that can be selected in a Material UI lab Autocomplete component
(for instance) I wish to limit user selecting only 3 options in my Autocomplete component, and disable the options when the length of TAG Array reaches 3.
The problem is there is no limitMaxNumberOfTags option in the api, and i cant get any way to access the Selected tags array whatsoever {except the limitTags, which only limits the visible tags}
.
something along the lines of this might help
getOptionDisabled={(options, tags) => (tags.length > 3 ? true : false)}
.
Here is my autocomplete implementation so far
<Autocomplete
multiple
id="tags-outlined"
options={students}
getOptionLabel={(option) => option.personalInfo.firstName + ' ' + option.personalInfo.lastName}
defaultValue={[...added]}
onChange={(e, newVal) => setAdded([...newVal])}
renderOption={(option, state) => {
return (
<Chip
icon={
<FaceIcon /> /*<Avatar color="primary" variant='outlined' size="small" className={classes.small}></Avatar>*/
}
label={option.personalInfo.firstName + ' ' + option.personalInfo.lastName}
color="default"
variant="outlined"
{...state}
/>
);
}}
renderTags={(options, getTagProps) =>
options.map((option) => (
<Chip
icon={
<FaceIcon /> /*<Avatar color="primary" variant='outlined' size="small" className={classes.small}></Avatar>*/
}
label={option.personalInfo.firstName + ' ' + option.personalInfo.lastName}
color="primary"
variant="outlined"
{...getTagProps({})}
/>
))
}
filterSelectedOptions
filterOptions={(options, state) =>
options.filter((option) => {
for (let i = 0; i < added.length; i++) {
if (added[i]._id === option._id) {
return false;
}
}
return true;
})
}
// ---> getOptionDisabled={(options) => (tags.length > 3 ? true : false)}
renderInput={(params) => (
<TextField {...params} variant="outlined" color="primary" label="Select Students" placeholder="Participant" />
)}
/>
Solution 1:[1]
Ran into similar issue recently. This is what I ended up doing. Basically you have to set the disabled flag on the chip itself directly, so it disables the text input, but not the chip. So you can still delete each chip.
export const AutoCompleteWithLimit: React.FC<Props> = ({
disabled = false,
limit = 2,
}) => {
const [disableInput, setDisableInput] = useState<boolean>(
value.length >= limit
);
return (
<Autocomplete
// Set disabled based on input
disabled={disabled || disableInput}
multiple
renderTags={(tagValue, getTagProps) =>
tagValue.map((option, index) => (
<Chip
key={index}
label={option.name}
{...getTagProps({ index })}
// Set disable explicitly after getTagProps
disabled={disabled}
/>
))
}
onChange={(_event: any, newValue: any[]) => {
// do something else
// set the disable input
setDisableInput(newValue.length >= limit);
}}
/>
);
};
Solution 2:[2]
Autocomplete added getOptionDisabled prop that can be used to disable all non selected options after maximum number of options is selected.
const a = ({limit = 3})=> {
const [limitReached, setLimitReached] = useState(false);
const [values, setValues] = useState([]);
const onSelect = useCallback((newValues) => {
setValues(newValues);
setLimitReached(newValues.length >= limit);
}, [limit]);
const checkDisable = useCallback(option => limitReached && !values.includes(option), [limitReached, values]);
return <Autocomplete
getOptionDisabled={checkDisable}
multiple
onChange={onSelect}
options={options}
value={values}
/>
}
Solution 3:[3]
I kind of found one solution to deal with it on my own, after fiddling through documentation for some unrelated Problem. The solution includes
- Setting
value = {[...myStateVariable]} - Using the reason parameter from the
onChange((event, newValue, reason)=>{....})callback
<Autocomplete
multiple
id="tags-outlined"
options={students}
getOptionLabel={(option) => option.personalInfo.firstName+' '+option.personalInfo.lastName}
defaultValue={[...added]}
value={[...added]}
onChange={(e, newVal, reason)=>{
if(reason==="select-option"){
addChips(newVal)
} else if(reason==="remove-option"){
handleDelete(newVal)
}
}}
The HandleDelete and addChips method are as follows.
const [added, setAdded] = useState([...added2])
const handleDelete = (students)=>{
setAdded([...students])
}
const addChips = (students)=>{
if(added.length>= maxParticipation){
alert('Cannot add more participants')
}else{
setAdded([...students])
}
}
the 'newValue' is first intercepted by the 'onChange' callback where it's length is evaluated and if the length is greater than a limiting value, the value update is cancelled. (Notice, the onChange that causes abortion of the process is only the one where "reason" = 'select-option' ). P.S- (forgot to mention about the disable Options query) the "disable" attribute on Options could also be manipulated in the renderOptions to disable all the options after "newVal" reaches a given length. [ solution for that(disable options) given in detail by @justindra ]
Solution 4:[4]
I used slice to limit the maximum limit on renderTags before rendering the chips
<Autocomplete
renderTags={(options, getTagProps) =>
options.slice(0, 5).map((option) => (
<Chip
icon={
<FaceIcon /> /*<Avatar color="primary" variant='outlined' size="small" className={classes.small}></Avatar>*/
}
label={option.personalInfo.firstName + ' ' + option.personalInfo.lastName}
color="primary"
variant="outlined"
{...getTagProps({})}
/>
))
}
/>
Solution 5:[5]
<Autocomplete
multiple
id="role"
options={rootStore.roleStore.listRole}
disableCloseOnSelect
value={rootStore.userStore.user.role}
onChange={(event, newValue) => {
setErrors({
...errors,
role: Utils.validate.single(
newValue,
Utils.constraintsUser.role
),
})
if (newValue.length > 2) {
alert("Please select max 2 labs")
} else {
rootStore.userStore.updateUser({
...rootStore.userStore.user,
role: newValue,
})
}
}}
getOptionLabel={(option) => option.description || ""}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox style={{ marginRight: 8 }} checked={selected} />
{option.description}
</React.Fragment>
)}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Labs"
placeholder="Labs"
/>
)}
/>
perfect solution..
Solution 6:[6]
My solution,I think it's the most properly way:
<Autocomplete
value={selectedTags}
onChange={(ev, value: Tag[]) => {
if (value.length <= 3) {
setSelectedTags(value);
} else {
return;
}
}}
/>;
Solution 7:[7]
In my case, this worked fine.
onChange={(e, newVal) => {
if (newVal > 3) {
newVal.pop();
}
setAdded([...newVal]);
}
Though the documentation explains that the value parameter of onChange prop is the new value, the actual content seems the array of all selected options.
And the last element in the value array is the newly selected option by the current change event. Therefore the code snippet above eliminates the exceeding element against the limited number (3 as above).
The latter type (Array<T>) for value parameter in the documentation is the case.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | justindra |
| Solution 2 | yehudaGold |
| Solution 3 | |
| Solution 4 | Hisham Haniffa |
| Solution 5 | appasaheb4 |
| Solution 6 | Li Mi |
| Solution 7 |
