K. D.
K. D.

Reputation: 4249

@material-ui Autocomplete: set input value programmatically

I have an asynchronous Autocomplete component that works fine so far.

Hopefully the simplified code is understandable enough:

export function AsyncAutocomplete<T>(props: AsyncAutocompleteProps<T>) {
  const [open, setOpen] = useState(false);

  const [options, setOptions] = useState<T[]>();
  const onSearch = (search: string) => {
    fetchOptions(search).then(setOptions);
  };

  return (
    <Autocomplete<T>
      open={open}
      onOpen={() => {
        setOpen(true);
      }}
      onClose={() => {
        setOpen(false);
      }}
      onChange={(event, value) => {
        props.onChange(value as T);
      }}
      getOptionSelected={props.getOptionSelected}
      getOptionLabel={props.getOptionLabel}
      options={options}
      value={(props.value as NonNullable<T>) || undefined}
      renderInput={(params) => (
        <TextField
          {...params}
          onChange={(event) => onSearch(event.currentTarget.value)}
        />
      )}
    />
  );
}

The component above works easily: when the user clicks on the input, the Autocomplete component displays an empty input field where the user can type in a value to search for. After the input has changed, the options are refetched to show matching results.

Now I want to add support for shortcodes: when the user types qq, the search term should be replaced by something, just like if the user would have typed something himself.

However, I found no way to update the value of the rendered TextField programmatically. Even if I set value directly on the TextField, it won't show my value but only the users input.

So, any ideas how to solve this problem?

Thank you very much.

What I've tried so far was to simply update the input within onKeyUp:

  // ...
  renderInput={(params) => (
    <TextInput
      {...params}
      label={props.label}
      onChange={(event) => onSearchChange(event.currentTarget.value)}
      InputProps={{
        ...params.InputProps,
        onKeyUp: (event) => {
          const value = event.currentTarget.value;
          if(value === 'qq') {
              event.currentTarget.value = 'something';
          }
        },
      }}
    />
  )}

With the code above I can see the something for a short time, but it gets replaced by the initial user input very soon.

Upvotes: 3

Views: 5496

Answers (1)

Prasad Phule
Prasad Phule

Reputation: 488

Autocomplete is useful for setting the value of a single-line textbox in one of two types of scenarios: combobox and free solo.

combobox - The value for the textbox must be chosen from a predefined set. You are using it so it not allowing you to add free text (onblur it replaced)

Answer: To take control of get and set value programmatically. you need a state variable.

Check here codesandbox code sample taken from official doc

Your code with my comment:-

export function AsyncAutocomplete<T>(props: AsyncAutocompleteProps<T>) {

  ... //code removed for brevity

  //This is a state variable to get and set text value programmatically.
  const [value, setValue] = React.useState({name: (props.value as NonNullable<T>) || undefined});
  
 return (
    <Autocomplete<T>

      ... //code removed for brevity

      //set value
      value={value}
      //get value
      onChange={(event, newValue) => setValue(newValue)}

      renderInput={(params) => (
         <TextInput
           {...params}
           label={props.label}
           onChange={(event) => onSearchChange(event.currentTarget.value)}
           InputProps={{
           ...params.InputProps,
           onKeyUp: (event) => {
           //get value
           const value = event.currentTarget.value;
           //if qq then set 'something'
           if (value === "qq") {
              setValue({ name: "something" });
           }
           //otherwise set user free input text
           else {
              setValue({ name: value });
           }
          },
       }}
      />
      )}
    />
  );
}

Upvotes: 2

Related Questions