如何使用 Material-Ui Autocomplete for Multi-Select 复选框实现 Formik 的 Field 组件?

Posted

技术标签:

【中文标题】如何使用 Material-Ui Autocomplete for Multi-Select 复选框实现 Formik 的 Field 组件?【英文标题】:How to implement Formik's Field component with Material-Ui Autocomplete for Multi-Select check boxes? 【发布时间】:2022-01-01 17:28:38 【问题描述】:

我正在尝试使用复选框实现 Formik 的 Field 组件和 Material-Ui 自动完成多个值。 每次我尝试从下拉列表中选择一个值时,它都会关闭弹出窗口,我必须再次打开它以进行下一个值选择。 由于自动完成的 onChange 行为,我正在使用 setFieldValue() 方法更新字段值。

import React,  useState, useEffect  from "react";
import  ErrorMessage, useField, Field, useFormikContext  from "formik";
import styles from "./Form.module.scss";
import  makeStyles, TextField, Checkbox  from "@material-ui/core";
import  Autocomplete  from "@material-ui/lab";
import service from "http-service";
import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank";
import CheckBoxIcon from "@material-ui/icons/CheckBox";

// Interface
interface IProps 
  name: string;
  rest?: any;
  className?: string;
  marginBottom?: string;
  label?: string;
  options?: any[];
  disabled?: boolean;
  tabIndex?: number;
  multiple?: boolean;
  returnArray?: boolean;
  referentialIdKey?: string;
  referentialNameKey?: string;
  serviceConfig?: string;
  query?: string;
  processResponse?: Function;


// Custom Style
const useStyles = makeStyles((theme) => (
  root: 
    "&  .MuiAutocomplete-inputRoot": 
      padding: "6px",
    ,
  ,
));

const AsyncSearchMultiSelect: React.FC<IProps> = (props) => 
  const 
    name,
    className,
    disabled,
    options,
    marginBottom,
    label,
    tabIndex,
    multiple,
    returnArray,
    referentialIdKey,
    referentialNameKey,
    serviceConfig,
    query,
    processResponse,
    ...rest
   = props;

  const  setFieldValue  = useFormikContext();
  const [field] = useField(name);
  const classes = useStyles();

  // States :
  const [asyncOptions, setAsyncOptions] = useState<any[]>([]);
  const [fetchedData, setFetchedData] = useState<any>();

  // all
  const [selectedOpt, setSelectedOpt] = useState<any>([]);

  // API Call for Dropdown Options :
  useEffect(() => 
    if (!serviceConfig) return;
    service[serviceConfig]
      .getDataByQuery(query || "")
      .then(( data :  data: any[] ) => 
        let dataSet = processResponse ? processResponse(data) : data;
        if (dataSet.data) 
          dataSet = dataSet.data;
        
        setFetchedData(dataSet);
        let tempOptions = dataSet.map((item: any) => 
          return 
            name: referentialNameKey ? item[referentialNameKey] : item["name"],
            value: referentialIdKey ? item[referentialIdKey] : item["id"],
           as never;
        );
        tempOptions.unshift( name: "All", value: "all" );
        console.log("tempOptions >>>> ", tempOptions);
        setAsyncOptions(tempOptions);
      )
      .catch((err: any) => console.log("Error! : ", err));
  , []);

  if (field.value.length > 0 && asyncOptions !== []) 
    let fullObjValues = asyncOptions.filter((option) =>
      field.value.includes(option.value)
    );
    console.log("fullObjValues >>> ", fullObjValues);
    if (fullObjValues.length > 0) 
      setFieldValue(name, fullObjValues);
    
  

  const handleChange = (event: any, newValue: any) => 
    console.log("AsyncSearchableEvent (value) >>> ", newValue);
    setFieldValue(name, newValue);
  ;

  const getOptionLabelCustom = (option: any) => 
    if (typeof option != "object") 
      let optionTitle: any = asyncOptions.find(
        (element:  value: any ) => element?.value == option
      );
      return optionTitle?.name;
     else 
      return option?.name;
    
  ;

  const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
  const checkedIcon = <CheckBoxIcon fontSize="small" />;

  const getComponent = () => 
    return (
      <Autocomplete
        // ...field
        disableCloseOnSelect
        multiple
        options=options ? options : asyncOptions
        value=field.value
        limitTags=2
        getOptionLabel=(option) => getOptionLabelCustom(option)
        onChange=(event, value) => handleChange(event, value)
        renderOption=(option,  selected ) => 
          // 'all' option
          const selectOptIndex = selectedOpt.findIndex(
            (opt: any) => opt.name.toLowerCase() === "all"
          );
          if (selectOptIndex > -1) 
            selected = true;
          
          //
          return (
            <React.Fragment>
              <Checkbox
                icon=icon
                checkedIcon=checkedIcon
                style= marginRight: 8 
                checked=selected
              />
              option.name
            </React.Fragment>
          );
        
        renderInput=(params) => (
          <TextField
            ...params
            label=label
            classes=classes
            fullWidth
            variant="outlined"
          />
        )
      />
    );
  ;

  return (
    <div
      className=styles.element_wrapper
      style= marginBottom: marginBottom 
    >
      <Field
        ...field
        id=name
        name=name
        disabled=disabled
        component=getComponent
        autoComplete="off"
      />
      <div className=styles.error>
        <ErrorMessage name=name />
      </div>
    </div>
  );
;

export default AsyncSearchMultiSelect; ```

【问题讨论】:

【参考方案1】:

尝试用 open 控制它:

 const [open, setOpen] = React.useState(false);

  <Autocomplete
            disabled=disabled
            id=name
            name=name
            sx=width: "100%"
            open=open
            onOpen=() => 
                setOpen(true);
            
            onClose=() => 
                setOpen(false);
            

【讨论】:

以上是关于如何使用 Material-Ui Autocomplete for Multi-Select 复选框实现 Formik 的 Field 组件?的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS + Material-UI:如何使用 Material-UI 的 <DatePicker> 将当前日期设置为默认值?

如何使用 Material-ui 更改 nowrap 文本

material-ui / 如何使用 'withStyles()' 设置 HOC 样式?

如何使用material-ui框架? [关闭]

如何使用 Material-UI 创建 onSubmit

ReactJS + Material-UI:如何减小 Material-UI 的 <TableRow/> 的列宽?