I'm using the Material-UI Autocomplete
in React for an address search/verification form, and I'd like there to always be a default option that shows up no matter the results.
In Angular I used [displayWith] with a function to tell it to always display an item from the array with a specific ID, but I'm not sure how to do it in React. I've been trying to figure out filterOptions
, but I don't want it to filter based on anything related to input, I just want it to always be an option.
In this simplified example, if I wanted it to always show the film with the year: 1111, how would I do that?
import React, { useState, ChangeEvent } from 'react';
import {
TextField,
InputAdornment
} from "@material-ui/core";
import Autocomplete from '@material-ui/lab/Autocomplete';
import { Search } from "@material-ui/icons";
import axios from "axios";
const AddressSearch = (props) => {
const [addressesList, setAddressesList] = useState([]);
const [inputAddress, setInputAddress] = useState<string>("");
const handleAddressChange = (event: ChangeEvent<{ value: unknown }>) => {
setInputAddress(event.target.value as string);
};
const topFilms = [
{ title: 'I HATE MOVIES', year: 1111 },
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather'},
{ title: 'The Godfather: Part II', year: 1974 }
]
return (
<div>
<Autocomplete
id="address-autocomplete"
freeSolo
options={topFilms}
getOptionLabel={(option) => option.title}
popupIcon={<Search />}
renderInput={(params) => <TextField
id="address-input"
{...params}
onChange={handleAddressChange}
placeholder="Quickly find your address"
InputProps={{ ...params.InputProps,
startAdornment: (
<InputAdornment position="start"><Search /></InputAdornment>
)}}
/> }
/>
</div>
);
}
export default AddressSearch;
Note: the website I'm working with is using Material-UI v 4.12
CodePudding user response:
You can create a wrapper of filterOptions
without touching anything in the original filterOptions
. Simply filter as normal, get the results and see if there is a default option and if there isn't, add it again:
import TextField from "@mui/material/TextField";
import Autocomplete, { createFilterOptions } from "@mui/material/Autocomplete";
const _filterOptions = createFilterOptions();
const filterOptions = (options, state) => {
const results = _filterOptions(options, state);
if (!results.includes(myDefaultOption)) {
results.unshift(myDefaultOption);
}
return results;
};
export default function ComboBox() {
return (
<Autocomplete
filterOptions={filterOptions}
options={options}
sx={{ width: 300 }}
renderInput={(params) => <TextField {...params} label="Movie" />}
/>
);
}
const myDefaultOption = { label: "My default option", year: 1994 };
const options = [
myDefaultOption,
...
]
Live Demo
CodePudding user response:
Edit: Bassed on your comment, you want to take the fixed values from the source, so I made some changes.
Edit: Added your year: 1111 example.
...
You can add filterOptions Docs to the Autocomplete element, then you add the fixed values to the result.
Filter options and source: Link to documentation
const filterOptions = createFilterOptions({
ignoreCase: true,
ignoreAccents: true
})
const topFilms = [
{ title: 'Elysium', year: 2013 },
{ title: 'I HATE MOVIES', year: 1111 },
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather' },
{ title: 'The Godfather: Part II', year: 1974 }
]
The addressesList state stores the titles of the values you want fixed, I took this hook from your code
const [addressesList, setAddressesList] = useState([1111])
Taking away the fixed values from the source, so they'll not repeat, this filter will only be triggered once, or if the source, or the "addressesList" changes.
const fixedValues = useMemo(() => topFilms.filter(e => addressesList.includes(e.year)), [topFilms, addressesList])
const dynamicValues = useMemo(() => topFilms.filter(e => !addressesList.includes(e.year)), [topFilms, addressesList])
Changes in the Autocomplete component.
<Autocomplete
filterOptions={(options, _ref) => [...fixedValues, ...filterOptions(options, _ref)]}
...Other attributes
/>
Final Code:
import React, { useState, ChangeEvent, useMemo } from 'react'
import { TextField, InputAdornment } from "@material-ui/core"
import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'
import { Search } from "@material-ui/icons"
const filterOptions = createFilterOptions({
ignoreCase: true,
ignoreAccents: true
})
const topFilms = [
{ title: 'Elysium', year: 2013 },
{ title: 'I HATE MOVIES', year: 1111 },
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather' },
{ title: 'The Godfather: Part II', year: 1974 }
]
const AddressSearch = (props) => {
const [addressesList, setAddressesList] = useState([1111])
const [inputAddress, setInputAddress] = useState("")
const handleAddressChange = (event) => {
setInputAddress(event.target.value)
}
const fixedValues = useMemo(() => topFilms.filter(e => addressesList.includes(e.year)), [topFilms, addressesList])
const dynamicValues = useMemo(() => topFilms.filter(e => !addressesList.includes(e.year)), [topFilms, addressesList])
return (
<div>
<Autocomplete
id="address-autocomplete"
freeSolo
options={dynamicValues}
getOptionLabel={(option) => option.title}
popupIcon={<Search />}
filterOptions={(options, _ref) => [...fixedValues, ...filterOptions(options, _ref)]}
renderInput={(params) => <TextField
id="address-input"
{...params}
onChange={handleAddressChange}
placeholder="Quickly find your address"
InputProps={{
...params.InputProps,
startAdornment: (
<InputAdornment position="start"><Search /></InputAdornment>
)
}}
/>}
/>
</div>
)
}
export default AddressSearch