Material ui Autocomplete press enter to create new chips

10,103

Solution 1

In case you have simple elements (not objects, just strings), and you don't really need to handle state (your autocomplete is not controlled) you can use the freeSolo prop of the Autocomplete.

<Autocomplete
    multiple
    freeSolo
    id="tags-outlined"
    options={["foo", "bar"]}
    defaultValue={["foo", "bar"]}
    renderInput={params => (
        <TextField
            {...params}
            variant="outlined"
            label="filterSelectedOptions"
            placeholder="Favorites"
        />
    )}
/>

In case your elements are more complex and you do need to control the element:

  1. Make sure the Autocomplete tag is a controlled one (you manage to value).

  2. Listen to key down event on the TextField.

  3. If the code is Enter (e.code === 'Enter') - take the value of the input and push it to the list of the current values that you have.

  4. Make sure you also handle the onChange to handle the removal of elements:

Here is the code:

const [autoCompleteValue, setAutoCompleteValue] = useState(["foo", "bar"]);

return (
  
  <Autocomplete
    multiple
    id="tags-outlined"
    options={[]}
    value={autoCompleteValue}
    onChange={(e, newval, reason) => {
      setAutoCompleteValue(newval);
    }}
    renderInput={params => (
      <TextField
        {...params}
        variant="outlined"
        label="filterSelectedOptions"
        placeholder="Favorites"
        onKeyDown={e => {
          if (e.code === 'enter' && e.target.value) {
            setAutoCompleteValue(autoCompleteValue.concat(e.target.value));
          }
        }}
      />
    )}
  />
);

Check the live working example of both options: https://codesandbox.io/s/mui-autocomplete-create-options-on-enter-gw1jc

Solution 2

To do this, don't use the Autocomplete element from MUI. Just use a a standard TextField with the use of InputProps. All you need to do is add a onKeyDown listener to the TextField that listens for 'Enter' and when the function is triggered, have it add to an array of Chips in the InputProps. It might look something like this:

const [inputValue, setInputValue] = useState('');
const [chips, setChips] = useState([])
const inputChange = ({target: {value}}) => {setInputValue(value)};
const handleKeyDown = ({key}) => {
 if(key === 'Enter') {
  setChips([...chips, inputValue])
 }
};
     <TextField
            fullWidth
            variant="outlined"
            label="Fish and Chips"
            value={inputValue}
            onChange={inputChange}
            multiline
            InputProps={{
              startAdornment: chips.map((item) => (
                <Chip
                  key={item}
                  label={item}
                />
              )),
            }}
          />

This is untested as written here, but it should work. I've done something similar in one of my apps.

Share:
10,103
Paul
Author by

Paul

Updated on June 09, 2022

Comments

  • Paul
    Paul almost 2 years

    I wish I could do such a thing using Autocomplete of material ui: wertarbyte

    That is, inserting text (string) without having a list of elements from which you must select.

    Therefore the noOptions message should not appear, every time the enter key is pressed on the keyboard the text is inserted.

    enter image description here

    Link: codesandbox

    Code:

    import React from "react";
    import Chip from "@material-ui/core/Chip";
    import Autocomplete from "@material-ui/lab/Autocomplete";
    import { makeStyles } from "@material-ui/core/styles";
    import TextField from "@material-ui/core/TextField";
    
    const useStyles = makeStyles(theme => ({
      root: {
        width: 500,
        "& > * + *": {
          marginTop: theme.spacing(3)
        }
      }
    }));
    
    export default function Tags() {
      const classes = useStyles();
    
      return (
        <div className={classes.root}>
          <Autocomplete
            multiple
            id="tags-outlined"
            options={[]}
            defaultValue={["foo", "bar"]}
            //getOptionLabel={(option) => option}
            //defaultValue={[top100Films[13]]}
            //filterSelectedOptions
            renderInput={params => (
              <TextField
                {...params}
                variant="outlined"
                label="filterSelectedOptions"
                placeholder="Favorites"
              />
            )}
          />
        </div>
      );
    }