AUTOCOMPLETE MUI not accepting values entered in the textfield - reactjs

i want the autocomplete to accept the value entered in the textfield even if it isn't present the dropdown.
my autocomplete component :
<Autocomplete
disabled={row.group_mapping_id && !('editClicked' in row)}
freeSolo
value={row.client_id}
onChange={(event, newValue) => props.onChangeTableInput('client_id', index, newValue, row)}
selectOnFocus
clearOnBlur
handleHomeEndKeys
options={props.clientIds}
renderOption={(props, option) => <li {...props}>{option.client_id}</li>}
autoHighlight
style={{width: '110px'}}
disableClearable
renderInput={(params) => <TextField {...params}
variant='outlined'
inputProps={{
...params.inputProps,
autoComplete: "disabled",
type: 'search'
}} />}
/>
clientIds array :
[{client_id : 'abcd'}, {client_id : 'abrd'}] and so on..
onChangeTableInput function :
const onChangeTableInput = (keyName, index, keyValue, rowData) => {
let temp_array = [...props.groupTableData];
let temp_value = keyValue
if(keyName === 'client_id'){
temp_array[index][keyName] = temp_value.client_id;
}else if(keyName === 'client_name'){
temp_array[index][keyName] = temp_value.client_name;
}else{
temp_array[index][keyName] = temp_value;
}
if (rowData && !rowData.group_mapping_id) {
temp_array[index]['operation'] = 'N';
}
props.setGroupTableData(temp_array);
}
i want the component to accept and display the typed option even if doesn't exist in the dropdown, right now if i don't type an existing option it doesn't display or read the value too.

<Autocomplete
{...otherProps}
defaultValue={{ client_id: row.client_id }}
getOptionLabel={option => option.client_id}
/>
And no need to renderOption={(props, option) => <li {...props}>{option.client_id}</li>}

Related

value returns 0 or undefined on MUI autocomplete unable to fetch the value in multiple attribute

On MUI Autocomplete component where the attribute is multiple, then the value prop returns 0 or undefined when we choose anyone of the selected option.
<Autocomplete
value={v.education}
onChange={handleEducationChange}
className={classes.textOutline}
multiple
id="virtualize-demo"
name="education"
style={{ width: 600 }}
disableCloseOnSelect
ListboxComponent={ListboxComponent}
options={educationList()}
getOptionLabel={(option) => option.title}
isOptionEqualToValue={(option, value) =>
option.title === value.title
}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.title}
</li>
)}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
name="education"
placeholder="Select your Qualification"
label="Education"
fullWidth
/>
)}
/>
// handleEducationChange
const initialState = { education: "", occupation: "" };
const [selectedOption, setSelectedOption] = useState([], initialState);
const handleEducationChange = (event) => {
const { name, values } = event.target;
setSelectedOption({ ...selectedOption, [name]: values });
console.log(values);
};
I need the selected value to be passed with value prop and that can be validated and posted to backend... kindly update the solution for this, Thanks.

MUI 5 Autocomplete choose attribute of object as value

My options looks like this
const options = [
{
"VaccinationType": "Sample"
},
{
"VaccinationType": "Another Sample"
}
]
Code:
const [vacType, setVacType] = useState('');
<Autocomplete
value={vacType}
onChange={(e, value) => {
console.log(value);
setVacType(value);
}}
options={options}
getOptionLabel={(option) => option.VaccinationType}
isOptionEqualToValue={(option, value) => option.VaccinationType === value.VaccinationType}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
size="small"
/>
)}
/>
I tried logging my value it still outputs the object { VaccinationType: "Sample" }. I want it to only output "Sample"
In MUI 4 I'm using this getOptionSelected={(option, value) => option?.VaccinationType === value?.VaccinationType}
Autocomplete options care about the item of the array which you put. According to your code, each item has all object values.
I searched to find a way but I couldn't.
There is a way probably you already thought;
You can edit options parameter like options={options.map(o => o.VaccinationType)} and remove VaccinationType key on getOptionLabel and isOptionEqualToValue
<Autocomplete
value={vacType}
onChange={(e, value) => {
console.log(value);
setVacType(value);
}}
options={options.map(o => o.VaccinationType)}
getOptionLabel={(option) => option}
isOptionEqualToValue={(option, value) =>
option === value
}
renderInput={(params) => (
<TextField {...params} variant="outlined" size="small" />
)}
/>
So I think u forgot to use onInputChange and InputValue properties for MUI Autocomplete, according to https://mui.com/material-ui/react-autocomplete/ these properties are needed in this case here u can read more https://mui.com/material-ui/api/autocomplete/ so please check this solution which works for me:
const [vacType, setVacType] = useState<any>('Sample')
const [inputValue, setInputValue] = useState('Sample')
const options: any[] = [
{
VaccinationType: 'Sample'
},
{
VaccinationType: 'Another Sample'
}
]
<Autocomplete
value={vacType} //here u set default Value and change whenever it changes a list choice
inputValue={inputValue} //here u set default Input and change whenever it changes by typing
onChange={(event: any, newValue: any | null) => {
setVacType(newValue)
}}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue)
}} // handles typing
options={options}
getOptionLabel={(option: { VaccinationType: string }) => {
return option?.VaccinationType
}}
isOptionEqualToValue={(option, value) =>
option?.VaccinationType === value?.VaccinationType
}
renderInput={params => (
<TextField {...params} variant="outlined" size="small" />
)}
/>
Hope it helps!
Also to answer your problem with console.log
your code:
onChange={(e, value) => {
console.log(value);
setVacType(value);
}}
exchanged to :
onChange={(event: any, newValue: any | null) => {
console.log('newValue')
console.log(newValue)
setVacType(newValue)
}}
gives object but you on this Autocomplete provide objects then seting values is in objects so u just need to add console.log(newValue.VaccinationType) but not for setVacType(newValue) and Autocomplete will remember last choice
I don't know why you want to do that!,
You can simply use value object wherever you want, and use any key of the object, take the value object as it's then use value.VaccinationType.
But, if you want to do this:
<Autocomplete
{...props}
value={options.find(op => op.VaccinationType === vacType)}
onChange={(e, value) => {
console.log(value); //Result: { "VaccinationType": "Sample" }
setVacType(value.VaccinationType); // <==== Here, add `.VaccinationType`
}}
/>

How to get mui Chip value before delete on Autocomplete freeSolo?

I'm working with Autocomplete and Chip component from the mui library. There's the DEMO (standard boilerplate).
I can't get the Chip contents before deleting it:
<Autocomplete
multiple
id="tags-filled"
options={top100Films.map((option) => option.title)}
defaultValue={[top100Films[1].title]}
freeSolo
onKeyDown={(prop) => {
if (prop.key === 'Enter') {
console.log(prop.target.value)
}
}}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
onDelete={(s) => console.log("the one", option)}
key={index} variant="outlined"
label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="freeSolo"
placeholder="Favorites"
/>
)}
/>
The issue is this
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
onDelete={(s) => console.log("the one", option)}
key={index} variant="outlined"
label={option} {...getTagProps({ index })} />
))
}
If I remove {...getTagProps({ index })} I do get the onDelete working the way I need it to but then the actual removal doesn't work. Again, the DEMO here
you can use
onChange={(e, value, situation, option) => {
if (situation === "removeOption") {
//write your code here
console.log("--->", e, value, situation, option);
}
setReceivers((state) => value);
}}
instead of the onDelete like this :
import * as React from "react";
import Chip from "#mui/material/Chip";
import Autocomplete from "#mui/material/Autocomplete";
import TextField from "#mui/material/TextField";
import Stack from "#mui/material/Stack";
export default function Tags() {
const [val, setVal] = React.useState({});
const [receivers, setReceivers] = React.useState([]);
console.log(receivers);
const handleClick = () => {
setVal(top100Films[0]); //you pass any value from the array of top100Films
// set value in TextField from dropdown list
};
return (
<Stack spacing={1} sx={{ width: 500 }}>
<Autocomplete
multiple
id="tags-filled"
options={top100Films.map((option) => option.title)}
defaultValue={[top100Films[13].title]}
freeSolo
onChange={(e, value, situation, option) => {
if (situation === "removeOption") {
console.log("--->", e, value, situation, option);
}
setReceivers((state) => value);
}}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
label={option}
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="freeSolo"
placeholder="Favorites"
/>
)}
/>
</Stack>
);
}
codesandbox

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" />
)}
/>
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);
}}
/>
);
};
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}
/>
}
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.
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 ]
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({})}
/>
))
}
/>
<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..
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;
}
}}
/>;

Set current location as default value in TextField Autocomplete Material UI

How to set defaultValue to current location into TexField inside AutoComplete form from Material UI.
I have fetched current location using Geolocaction
navigator.geolocation.getCurrentPosition(function(position) {
console.log("position",position);
console.log("Latitude is :", position.coords.latitude);
console.log("Longitude is :", position.coords.longitude);
// Get address from latidude & longitude.
Geocode.fromLatLng(position.coords.latitude, position.coords.longitude).then(
response => {
setAddress(response.results[0].formatted_address);
},
error => {
console.error(error);
}
);
});
I am using autocomplete form from MATERIAL UI. I have tried to write defaultValue={addresss} but nothing happens.
<Autocomplete
id="google-map-demo"
getOptionLabel={(option) =>
typeof option === "string" ? option : option.description
}
filterOptions={(x) => x}
options={options}
onChange={(e, v) => {
user.setFulladdress(v.description);
getPlace(v.place_id);
}}
renderInput={(params) => (
<TextField
{...params}
placeholder="Address"
defaultValue={address}
InputProps={{
...params.InputProps,
classes: {
input: classes.resize,
},
disableUnderline: true,
}}
variant="standard"
fullWidth
onChange={(e, v) => {
setInputValue(e.target.value);
}}
/>
Anyone idea how to put default value as current location of user?

Resources