Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
968 views
in Technique[技术] by (71.8m points)

javascript - 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);
    }
};
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...