How to use Material-UI Autocomplete component to select multiple options and add new options?

Viewed 18236

goal

I have a multiple field that:

  1. we can select users present in the autocompletion (a list that I retrieve via API)
  2. We can enter the names of new users that we separate them with a comma

For that, I therefore need to retrieve the value of value and inputValue. For value, there is no problem but for inputValue, there is something that I do not understand

problem

When I modify the value of inputValue in onInputChange, its state is modified and reseted like that

information

I base on the example that material UI offers

Code in Sandbox

Code

import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";

interface User {
  id: number;
  name: string;
}

const userList: User[] = [
  { id: 1, name: "name 1" },
  { id: 2, name: "name 2" },
  { id: 3, name: "name 3" }
];

export default function AutocompleteControlled() {

  const [users, setUsers] = React.useState<number[]>([]);
  const [inputValue, setInputValue] = React.useState("");

  return (
    <div>
      <br />
      <Autocomplete
        multiple
        value={userList.filter((el) => users.includes(el.id))}
        onChange={(event: any, newValue: User[]) => {
          setUsers(newValue.map((el) => el.id));
        }}
        inputValue={inputValue}
        onInputChange={(event: any, newInputValue: string) => {
          console.log("newInputValue", newInputValue);
          setInputValue(newInputValue);
        }}
        id="controllable-states-demo"
        options={userList}
        getOptionLabel={(option) => option.name}
        renderInput={(params) => (
          <TextField {...params} label="Controllable" variant="outlined" />
        )}
      />
    </div>
  );
}

All comments are welcome :)

1 Answers

You want to enable multi selection, and allow the user to enter any arbitrary value in the textbox. Therefore you can use the Autocomplete component with the freesolo prop set to true, so the textbox can contain any arbitrary value.

This is the Material-UI example closest to your use case.

We will use a controlled component, so you can control its behavior with the value and onChange props. Check the code below.

You can select from the pre-populated items, or you can enter any arbitrary value and press enter, and a chip will be added to the component, and the value will be added to the array in the state.

Currently value is an array of the names but you can set it to the ids or whatever you want.

Try this code:

import React from "react";
import Chip from "@material-ui/core/Chip";
import Autocomplete from "@material-ui/lab/Autocomplete";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      width: 500,
      "& > * + *": {
        marginTop: theme.spacing(3),
      },
    },
  })
);

interface User {
  id: number;
  name: string;
}

const userList: User[] = [
  { id: 1, name: "name 1" },
  { id: 2, name: "name 2" },
  { id: 3, name: "name 3" },
  { id: 4, name: "name 4" },
  { id: 5, name: "name 5" },
];

export default function AutocompleteControlled() {
  const classes = useStyles();

  const [value, setValue] = React.useState<any>([userList[0].name]);

  console.log("value: ", value);

  return (
    <div className={classes.root}>
      <Autocomplete
        value={value}
        onChange={(event, newValue) => {
          setValue(newValue);
        }}
        multiple
        id="tags-filled"
        options={userList.map((option) => option.name)}
        freeSolo
        renderTags={(value: string[], getTagProps) =>
          value.map((option: string, index: number) => (
            <Chip
              variant="outlined"
              label={option}
              {...getTagProps({ index })}
            />
          ))
        }
        renderInput={(params) => (
          <TextField
            {...params}
            variant="filled"
            label="Users"
            placeholder="Search"
          />
        )}
      />
    </div>
  );
}

This is how it looks, the controlled value is logged to the console.

screenshot

Related