Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material ui Autocomplete: can tags be created on events aside from 'Enter' events?

I am currently working with the freesolo Autocomplete and my particular use case requires tags to be created when commas or spaces follow the input text. Autocomplete currently creates tags on the Enter event, but I don't think there is anything built into Autocomplete yet that supports tag creation on any other event. I'm wondering if I'm missing anything, or if I'm not, how could I approach this problem?

Currently, I'm attempting to use the onInputChange attribute in Autocomplete to capture the string coming in. I check that string for commas and spaces, and on a successful find of one of those characters I manually fire off the Enter event using some native JS code. This works in some cases, but not in all cases and accounting for all cases is becoming tedious. This approach seems like it's prone to a lot of issues, and I'm not convinced it's the best way to go about implementing tag creation on different events. Looking for some thoughts. Thanks

onInputChange attribute usage:

<Autocomplete
        multiple
        freeSolo
        filterSelectedOptions
        id="auto-complete"
        options={foo.map(bar => bar.name)}
        ref={autoRef}
        onInputChange={(e, value) => {
            createTagOnEvent(value);
        }}/>

Searching through input for commas and spaces and firing off Enter event manually:

const createTagOnEvent = (bar) => {
    if (pattern.test(bar)) {
        const ke = new KeyboardEvent("keydown", {bubbles: true, cancelable: true, keyCode: 13});
        autoRef.current.dispatchEvent(ke);
    }
};
like image 243
samslei Avatar asked Nov 25 '19 16:11

samslei


1 Answers

Below is the approach I would recommend.

There are two main aspects to the approach:

  1. Use a "controlled" input approach for the Autocomplete so that you have full control over the current value.

  2. Specify the onKeyDown handler for the TextField input via params.inputProps.onKeyDown with appropriate logic for adding the new value.

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

export default function Tags() {
  const [value, setValue] = React.useState([top100Films[13]]);
  const handleKeyDown = event => {
    switch (event.key) {
      case ",":
      case " ": {
        event.preventDefault();
        event.stopPropagation();
        if (event.target.value.length > 0) {
          setValue([...value, event.target.value]);
        }
        break;
      }
      default:
    }
  };
  return (
    <div style={{ width: 500 }}>
      <Autocomplete
        multiple
        freeSolo
        id="tags-outlined"
        options={top100Films}
        getOptionLabel={option => option.title || option}
        value={value}
        onChange={(event, newValue) => setValue(newValue)}
        filterSelectedOptions
        renderInput={params => {
          params.inputProps.onKeyDown = handleKeyDown;
          return (
            <TextField
              {...params}
              variant="outlined"
              label="filterSelectedOptions"
              placeholder="Favorites"
              margin="normal"
              fullWidth
            />
          );
        }}
      />
    </div>
  );
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
  { title: "The Shawshank Redemption", year: 1994 },
  { title: "The Godfather", year: 1972 },
// ... many more options
];

Edit Autocomplete keydown override

Here's a Typescript version:

/* eslint-disable no-use-before-define */
import React from "react";
import TextField from "@material-ui/core/TextField";
import Autocomplete, { RenderInputParams } from "@material-ui/lab/Autocomplete";

interface ObjectOption {
  title: string;
  year: number;
}
type Option = ObjectOption | string;

interface MyInputProps {
  onKeyDown: (event: object) => void;
}
interface MyParams extends RenderInputParams {
  inputProps: MyInputProps;
}

export default function Tags() {
  const [value, setValue] = React.useState([top100Films[13]]);
  const handleKeyDown = event => {
    switch (event.key) {
      case ",":
      case " ": {
        event.preventDefault();
        event.stopPropagation();
        if (event.target.value.length > 0) {
          setValue([...value, event.target.value]);
        }
        break;
      }
      default:
    }
  };
  return (
    <div style={{ width: 500 }}>
      <Autocomplete
        multiple
        freeSolo
        id="tags-outlined"
        options={top100Films}
        getOptionLabel={option => {
          if (typeof option === "string") {
            return option;
          }
          return option.title;
        }}
        value={value}
        onChange={(event, newValue) => setValue(newValue)}
        filterSelectedOptions
        renderInput={(params: MyParams) => {
          params.inputProps.onKeyDown = handleKeyDown;
          return (
            <TextField
              {...params}
              variant="outlined"
              label="filterSelectedOptions"
              placeholder="Favorites"
              margin="normal"
              fullWidth
            />
          );
        }}
      />
    </div>
  );
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films: ObjectOption[] = [
  { title: "The Shawshank Redemption", year: 1994 },
  { title: "The Godfather", year: 1972 },
// ... many more options
];

Edit Typescript Autocomplete override keydown

like image 182
Ryan Cogswell Avatar answered Nov 14 '22 23:11

Ryan Cogswell