【问题标题】:With react-select, select multiple items matching search simultaneously使用react-select,同时选择多个匹配搜索的项目
【发布时间】:2018-10-10 17:46:44
【问题描述】:

我正在使用react-select 显示可搜索的项目下拉列表,用户可以从中选择多个项目。我的列表很长,用户经常希望多选许多匹配相同过滤字符串的项目,这是一个有点乏味的过程,因为每次选择一个项目时,下拉菜单都会消失,您需要重新键入搜索。

例如,以下沙盒有一个react-select,其中列出了许多苹果和奶酪。为了选择所有苹果,必须不断输入“Apple”并一次选择一个苹果。

https://codesandbox.io/s/2l99lry5p

来自桌面 UI 背景,我自然希望能够键入搜索查询并按 Ctrl-A 选择所有匹配的搜索结果并将它们添加到我的列表中,或者 Ctrl-Click 从中挑选多个项目匹配集。但据我所知,react-select 中不支持任何这样的热键。

react-select API 是否有任何方法可以实现“全选”热键,该热键将选择与当前搜索过滤器匹配的所有内容(甚至页面上的显式“全选”按钮也可以) ?我看不到任何编程方式来访问与过滤器匹配的对象集。这是我需要 fork react-select 来实现的,还是可以通过现有的 API 以某种方式做到这一点?

【问题讨论】:

标签: javascript reactjs react-select


【解决方案1】:

React Select 具有内置的道具,可用于防止菜单在选择时关闭并保留搜索字符串。

首先通过将closeMenuOnSelect 设置为false 来防止菜单在选择时关闭。

如果操作等于'input-change',则onInputChange 将搜索字符串存储在state 中。

inputValue 设置为this.state.value 会将搜索字符串保留在输入字段中。

class Foo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: ''
        };
    }
    handleInputChange = (value, e) => {
        if (e.action === 'input-change') {
            this.setState({value});
        }
    }
    render() {
        return (
            <Select
                isMulti
                name="colors"
                options={options.map(x => MakeOption(x))}
                className="basic-multi-select"
                classNamePrefix="select"
                /* added these props */
                closeMenuOnSelect={false}
                onInputChange={this.handleInputChange}
                inputValue={this.state.value}
            />
        )
    }
}

更新的沙盒: https://codesandbox.io/s/yvmzx6pn6z

【讨论】:

  • 这绝对是一个改进,但仍然不是很容易选择所有的苹果,它需要我点击每个搜索结果。我能够将我的示例修改为onInputChange 处理程序将匹配结果列表打印到控制台的位置,并尝试通过将结果通过state 推回@987654332 来创建一个“选择所有匹配项”的按钮@ prop 的 react-select,如果可行的话,也许会发布一个自我回答。我遇到的一个问题是onInputChange 在填充匹配结果之前发生,并且似乎没有过滤后事件。
【解决方案2】:

我破解了一些我想要的但很丑的东西:

https://codesandbox.io/s/j7453qrmv

使用方法:

  • 尝试搜索“apple”,然后按“将所有匹配项添加到选择”

方法:

  • 正如@wdm 提到的,有一个onInputChanged 你可以加入。
  • onInputChanged中,获取匹配项存储在组件的状态中
  • 我在Select 附近添加了一个按钮,允许用户选择将匹配的项目集复制到另一个状态变量chosenItems
  • react-selectSelect 组件有一个value 属性,您可以提供该属性以编程方式选择项目。我将state.chosenItems 传递给此。

这可行,但有很多事情让这很痛苦:

  • onInputChanged 处理程序在匹配过滤器的项目出现之前被调用。我试图通过 DOM 查询来获取匹配的项目,但没有成功,因为 onInputChanged 为时过早。因此,我没有依赖react-select 的过滤逻辑,而是在onInputChanged 处理程序中复制过滤逻辑。这不是很好,因为我的过滤逻辑和显示的匹配项列表之间可能存在差异。
  • 每当您在输入搜索后单击时,react-select 都会清除搜索,这会再次调用 onInputChanged 事件。因此,通过单击自定义“添加所有匹配项”按钮,它会删除过滤器、清除列表、调用 onInputChanged 并使用新的匹配项列表设置状态。为了解决这个问题,我需要一个previousMatchingOptions 状态变量来跟踪上次调用onInputChanged 中的匹配项。这似乎是一个可怕的 hack。
  • 同样,我尝试根据当前是否有多个与搜索匹配的项目来隐藏/显示我的“选择所有匹配项目”按钮,但我同样因时间问题而受挫。我试图解决这个问题,但一直陷入困境。
  • 我为“选择所有匹配项”设计的 UI 感觉与 react-select 集成得并不好。如果它是他们组件的一部分而不是在它旁边会更好。
  • 通过使用Select 组件的values 属性,您绕过了组件对其状态的内部管理,因此添加、删除和清除项目的常规方法如果不重新实现自定义项就无法正常工作onChange 处理程序修改传递给valuesstate.chosen。自己管理似乎也不太理想。

所以,我有一个解决方案,但如果有人有更清晰和/或更简单的建议,我会很高兴听到它!

似乎分叉控件并在组件内部进行这些更改可能是更好的方法。

  • 在我的onInputChanged 中,我尝试使用一些getElementsByClassNamehackery 直接从DOM 获取匹配的搜索结果,尽管这种方法不起作用,因为onInputChanged

【讨论】:

    【解决方案3】:

    实现“全选”选项的一种非常简单的方法是使用自定义组件覆盖 React-Select 组件。为此,您首先需要将其导入为

    import { default as ReactSelect } from 'react-select';
    

    然后创建一个自定义组件,该组件定义一个名为“allowSelectAll”的新属性,并在此属性设置为“true”时选择所有选项。

    const Select = props => {
        if (props.allowSelectAll) {
            if (props.value && (props.value.length === props.options.length)) {
                return (
                    <ReactSelect
                        {...props}
                        value={[props.allOption]}
                        onChange={selected => props.onChange(selected.slice(1))}
                    />
                );
            }
    
            return (
                <ReactSelect
                    {...props}
                    options={[props.allOption, ...props.options]}
                    onChange={selected => {
                        if (
                            selected.length > 0 &&
                            selected[selected.length - 1].value === props.allOption.value
                        ) {
                            return props.onChange(props.options);
                        }
                        return props.onChange(selected);
                    }}
                />
            );
        }
    
        return <ReactSelect {...props} />;
    };
    
    Select.propTypes = {
        options: PropTypes.array,
        value: PropTypes.any,
        onChange: PropTypes.func,
        allowSelectAll: PropTypes.bool,
        allOption: PropTypes.shape({
            label: PropTypes.string,
            value: PropTypes.string
        })
    };
    
    Select.defaultProps = {
        allOption: {
            label: "Select all",
            value: "*"
        }
    };
    

    注意:您可以简单地复制并粘贴上面给出的代码,它绝对可以正常工作。

    完成后,您可以简单地使用新的“Select”组件,并将“allowSelectAll”属性设置为 true。

    <Select allowSelectAll={true} isMulti={true} isSearchable={true} options={options} />
    

    【讨论】:

    • 你还需要导入PropTypes
    • 我无法复制粘贴此代码并使其正常工作。但是,如果我理解您的代码的意图,我认为它不能解决我最初的问题,因为它“选择所有”(来自原始集合),而我正在寻找“选择所有匹配项”的解决方案(所有匹配项当前搜索过滤器)。仅供参考,我在下面提供了我自己的问题的答案,该答案与您的答案相似(在控件中添加“选择所有匹配项”按钮),但对我的结果不太满意。
    【解决方案4】:

    您可以像这样使用filterOption 函数:

    <select
      options={[{label: 'test', value: 1, customData: 'bla blub test'}]}
      filterOption={(option, filter) => {
        const { customData } = option.customData;
    
        if(customData.toLowerCase().indexOf(filter.toLowerCase()) >= 0) {
          return true;
        }
      }} />
    

    希望对你有帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多