Code Ninja
Code Ninja

Reputation: 143

MUI Autocomplete single array

I am trying to apply the principle of Add xyx in MUI Autocomplete in Reactjs, using single dimension i.e [1,2,3,4,5], i.e. without objects, So far I have got the examples and references with objects only. Is there any way possible to achieve so, for reference I am trying to achieve this using only single array, and I want the Add <YOUR_SEARCH_TERM> option, I have so far got only the input search term, I want to append Add as a prefix but if I add that Add, it also reflects in the input box as well.

Upvotes: 1

Views: 3831

Answers (1)

jeffreyquan
jeffreyquan

Reputation: 768

There is a way you can achieve this. Please see the Code Sandbox example I created. I modified the Material UI example you had linked.

In this example, the options are numbers in an array, instead of objects i.e. [0, 1, 2, 3, 4, 5].

There are three steps:

  1. To include the suggestion to Add the user's input in the filtered options, we push the user's input with the Add prefix.
filterOptions={(options, params) => {
  const filtered = filter(options, params);

  const { inputValue } = params;
  // Suggest the creation of a new value
  const isExisting = options.some((option) => inputValue === option);
  if (inputValue !== "" && !isExisting) {
    filtered.push(`Add ${inputValue}`);
  }

  return filtered;
}}
  1. As you rightly pointed out, if you select this, the Add prefix will also appear in the input box. For example, if we select Add 6, the input box will show Add 6. We only want to show 6.

    We can achieve this by adding a replace in the getOptionLabel function. This controls how the option is rendered. Here we are replacing Add from the option we selected with an empty string. This removes the Add prefix and the space between the prefix and the input. For example, instead of Add 6 being displayed, only 6 will be displayed.

getOptionLabel={(option) => {
  // Value selected with enter, right from the input
  if (typeof option === "string") {
    const updatedOption = option.replace("Add ", "");
    return updatedOption;
  }
  // Add "xxx" option created dynamically
  if (option.inputValue) {
    return option.inputValue;
  }
  // Regular option
  return option.toString();
}}

  1. We also don't want to save the value with the Add prefix when we setValue in our onChange function. Similar to Step 2, we have to add a replace in the onChange function to remove the Add prefix.
onChange={(event, newValue) => {
  if (typeof newValue === "string") {
    const updatedValue = newValue.replace("Add ", "");
    setValue(updatedValue);
  } else if (newValue && newValue.inputValue) {
    // Create a new value from the user input
    setValue(newValue.inputValue);
  } else {
    setValue(newValue);
  }
}}

Putting it altogether:

import * as React from "react";
import TextField from "@mui/material/TextField";
import Autocomplete, { createFilterOptions } from "@mui/material/Autocomplete";
import "./styles.css";

const numbers = [0, 1, 2, 3, 4, 5];
const filter = createFilterOptions();

export default function App() {
  const [value, setValue] = React.useState(null);

  return (
    <div className="App">
      <Autocomplete
        value={value}
        onChange={(event, newValue) => {
          if (typeof newValue === "string") {
            const updatedValue = newValue.replace("Add ", "");
            setValue(updatedValue);
          } else if (newValue && newValue.inputValue) {
            // Create a new value from the user input
            setValue(newValue.inputValue);
          } else {
            setValue(newValue);
          }
        }}
        filterOptions={(options, params) => {
          const filtered = filter(options, params);

          const { inputValue } = params;
          // Suggest the creation of a new value
          const isExisting = options.some((option) => inputValue === option);
          if (inputValue !== "" && !isExisting) {
            filtered.push(`Add ${inputValue}`);
          }

          return filtered;
        }}
        selectOnFocus
        clearOnBlur
        handleHomeEndKeys
        id="free-solo-with-text-demo"
        options={numbers}
        getOptionLabel={(option) => {
          // Value selected with enter, right from the input
          if (typeof option === "string") {
            const updatedOption = option.replace("Add ", "");
            return updatedOption;
          }
          // Add "xxx" option created dynamically
          if (option.inputValue) {
            return option.inputValue;
          }
          // Regular option
          return option.toString();
        }}
        renderOption={(props, option) => <li {...props}>{option}</li>}
        sx={{ width: 300 }}
        freeSolo
        renderInput={(params) => (
          <TextField {...params} label="Array of numbers demo" />
        )}
      />
    </div>
  );
}

Upvotes: 4

Related Questions