antd系列之Select

search-select

实例

Example1 - 远程搜索

import React, { useState, useRef, useMemo, ReactNode } from "react";
import ReactDOM from "react-dom";
import debounce from "lodash/debounce";
import { Select, Spin } from "antd";
import { SelectProps } from "antd/es/select";

import "antd/dist/antd.css";
import "./index.css";

export interface SearchSelectProps<ValueType = any>
  extends Omit<SelectProps<ValueType>, "options" | "children"> {
  fetchOptions: (search: string) => Promise<ValueType[]>;
  debounceTimeout?: number;
}

function SearchSelect<
  ValueType extends {
    key?: string;
    label: ReactNode;
    value: string | number;
  } = any
>({ fetchOptions, debounceTimeout = 800, ...props }: SearchSelectProps) {
  const [fetching, setFetching] = useState(false);
  const [options, setOptions] = useState<ValueType[]>([]);
  const fetchRef = useRef(0);

  const searchFetcher = useMemo(() => {
    const loadOptions = (value: string) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);

      fetchOptions(value).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          // for fetch callbacl order
          return;
        }

        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);

  return (
    <Select<ValueType>
      showSearch
      labelInValue
      filterOption={false}
      onSearch={searchFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      {...props}
      options={options}
    />
  );
}

// Usage of searchSelect
interface searchSelectUserValue {
  label: string;
  value: string;
}

async function fetchUserList(
  username: string
): Promise<searchSelectUserValue[]> {
  console.log("fetching user", username);

  // 本地mock
  return new Promise((resolve, reject) => {
    const response = [
      { label: "张三", value: "zhansan" },
      { label: "李四", value: "lisi" },
      { label: "王五", value: "wangwu" }
    ];

    resolve(response);
  });
  // return fetch('url').then((res) => {});
}

const SearchSelectDemo = () => {
  const [value, setValue] = useState<searchSelectUserValue[]>([]);

  return (
    <div>
      <SearchSelect
        className="search-select"
        mode="multiple"
        value={value}
        placeholder="请输入搜索条件"
        fetchOptions={fetchUserList}
        onChange={(newValue) => {
          setValue(newValue);
        }}
      />
    </div>
  );
};

ReactDOM.render(<SearchSelectDemo />, document.getElementById("root"));

Example2 - 本地搜索

import React, { useState, useRef, useMemo, ReactNode } from "react";
import ReactDOM from "react-dom";
import debounce from "lodash/debounce";
import { Select, Spin } from "antd";
import { SelectProps } from "antd/es/select";

import "antd/dist/antd.css";
import "./index.css";

export interface SearchSelectProps<ValueType = any>
  extends Omit<SelectProps<ValueType>, "options" | "children"> {
  fetchOptions: (search: string) => Promise<ValueType[]>;
  debounceTimeout?: number;
}

function SearchSelect<
  ValueType extends {
    key?: string;
    label: ReactNode;
    value: string | number;
  } = any
>({ fetchOptions, debounceTimeout = 800, ...props }: SearchSelectProps) {
  const [fetching, setFetching] = useState(false);
  const [options, setOptions] = useState<ValueType[]>([]);
  const fetchRef = useRef(0);

  const searchFetcher = useMemo(() => {
    const loadOptions = (value: string) => {
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);

      fetchOptions(value).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          // for fetch callbacl order
          return;
        }

        setOptions(newOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
  }, [fetchOptions, debounceTimeout]);

  return (
    <Select<ValueType>
      showSearch
      labelInValue
      filterOption={true}
      onSearch={searchFetcher}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      {...props}
      options={options}
    />
  );
}

// Usage of searchSelect
interface searchSelectUserValue {
  label: string;
  value: string;
}

async function fetchUserList(
  username: string
): Promise<searchSelectUserValue[]> {
  console.log("fetching user", username);

  // 本地mock
  return new Promise((resolve, reject) => {
    const response = [
      { label: "张三", value: "zhansan" },
      { label: "李四", value: "lisi" },
      { label: "王五", value: "wangwu" }
    ];

    resolve(response);
  });
  // return fetch('url').then((res) => {});
}

const SearchSelectDemo = () => {
  const [value, setValue] = useState<searchSelectUserValue[]>([]);

  return (
    <div>
      <SearchSelect
        className="search-select"
        mode="multiple"
        value={value}
        placeholder="请输入搜索条件"
        fetchOptions={fetchUserList}
        onChange={(newValue) => {
          setValue(newValue);
        }}
      />
    </div>
  );
};

ReactDOM.render(<SearchSelectDemo />, document.getElementById("root"))

参数说明

参数说明类型默认值版本
filterOption是否根据输入项进行筛选。当其为一个函数时,会接收 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 falseboolean | function(inputValue, option)true
labeInValue是否把每个选项的label包装到value中,会把Select的value类型从string变为{value: string, label: ReactNode}的格式booleanfalse-

源码分析

  • ant-design 使用的是rc-select去作为基本的组件,filterOption的解析是获取之后走到了rc-select之中了。
// 源码位置 https://github.com/react-component/select/blob/13.x/src/utils/valueUtil.ts
/** Filter single option if match the search text */


function getFilterFunction(optionFilterProp) {
  return function (searchValue, option) {
    var lowerSearchText = searchValue.toLowerCase(); // Group label search

    if ('options' in option) {
      return toRawString(option.label).toLowerCase().includes(lowerSearchText);
    } // Option value search


    var rawValue = option[optionFilterProp];
    var value = toRawString(rawValue).toLowerCase();
    return value.includes(lowerSearchText);
  };
}


function filterOptions(searchValue, options, _ref6) {
  var optionFilterProp = _ref6.optionFilterProp,
      filterOption = _ref6.filterOption;
  var filteredOptions = [];
  var filterFunc;

  if (filterOption === false) {
    return (0, _toConsumableArray2.default)(options);
  }

  if (typeof filterOption === 'function') {
    filterFunc = filterOption;
  } else {
    filterFunc = getFilterFunction(optionFilterProp);
  }

  options.forEach(function (item) {
    // Group should check child options
    if ('options' in item) {
      // Check group first
      var matchGroup = filterFunc(searchValue, item);

      if (matchGroup) {
        filteredOptions.push(item);
      } else {
        // Check option
        var subOptions = item.options.filter(function (subItem) {
          return filterFunc(searchValue, subItem);
        });

        if (subOptions.length) {
          filteredOptions.push((0, _objectSpread2.default)((0, _objectSpread2.default)({}, item), {}, {
            options: subOptions
          }));
        }
      }

      return;
    }

    if (filterFunc(searchValue, injectPropsWithOption(item))) {
      filteredOptions.push(item);
    }
  });
  return filteredOptions;
}
  • 为什么会去处理filterOptions了?因为进入的时候做了一步generate操作,也就是rc-select SelectProps是generate中 import type { SelectProps, RefSelectProps } from './generate';
// https://github.com/react-component/select/blob/13.x/src/generate.tsx
    // Display options for OptionList
    const displayOptions = useMemo<OptionsType>(() => {
      if (!mergedSearchValue || !mergedShowSearch) {
        return [...mergedOptions] as OptionsType;
      }
      const filteredOptions: OptionsType = filterOptions(mergedSearchValue, mergedOptions, {
        optionFilterProp,
        filterOption: mode === 'combobox' && filterOption === undefined ? () => true : filterOption,
      });
      if (
        mode === 'tags' &&
        filteredOptions.every((opt) => opt[optionFilterProp] !== mergedSearchValue)
      ) {
        filteredOptions.unshift({
          value: mergedSearchValue,
          label: mergedSearchValue,
          key: '__RC_SELECT_TAG_PLACEHOLDER__',
        });
      }
      if (filterSort && Array.isArray(filteredOptions)) {
        return ([...filteredOptions] as OptionsType).sort(filterSort);
      }

      return filteredOptions;
    }, [mergedOptions, mergedSearchValue, mode, mergedShowSearch, filterSort]);

    const popupNode = (
      <OptionList
        {...}
        options={displayOptions}
        {...}
      />
    );
// vs
    var displayOptions = (0, React.useMemo)(function () {
      if (!mergedSearchValue || !mergedShowSearch) {
        return (0, _toConsumableArray2.default)(mergedOptions);
      }

      var filteredOptions = filterOptions(mergedSearchValue, mergedOptions, {
        optionFilterProp: optionFilterProp,
        filterOption: mode === 'combobox' && filterOption === undefined ? function () {
          return true;
        } : filterOption
      });

      if (mode === 'tags' && filteredOptions.every(function (opt) {
        return opt[optionFilterProp] !== mergedSearchValue;
      })) {
        filteredOptions.unshift({
          value: mergedSearchValue,
          label: mergedSearchValue,
          key: '__RC_SELECT_TAG_PLACEHOLDER__'
        });
      }

      if (filterSort && Array.isArray(filteredOptions)) {
        return (0, _toConsumableArray2.default)(filteredOptions).sort(filterSort);
      }

      return filteredOptions;
    }, [mergedOptions, mergedSearchValue, mode, mergedShowSearch, filterSort]);

    var popupNode = /*#__PURE__*/React.createElement(OptionList, {
      {...}
      options: displayOptions,
      {...}
    }); // ============================= Clear ==============================
  • filterOption: mode === 'combobox' && filterOption === undefined ?function () { return true; } : filterOption 这段代码也就说明了如果我不设置filterOption的话,它默认是true的缘由。

Thinking in JackDan

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现 antdvue2 中 Select 组件的全选功能,可以在 Select 组件中添加一个多选框来控制全选状态,同时通过监听 Select 组件的 value 值的变化来实现选中所有选项或取消选中所有选项。 具体实现步骤如下: 1. 在 Select 组件上方添加一个 Checkbox 组件,用来控制全选状态。 2. 通过 Select 组件的 options 属性提供选项列表。 3. 监听 Select 组件的 value 值的变化,当 value 值与 options 中所有选项的 value 值一致时,将 Checkbox 组件的选中状态置为 true,否则置为 false。 4. 监听 Checkbox 组件的 onChange 事件,当 Checkbox 组件的选中状态发生变化时,根据选中状态修改 Select 组件的 value 值。 示例代码如下: ```vue <template> <div> <a-checkbox @change="handleCheckAllChange" :checked="isCheckAll">全选</a-checkbox> <a-select v-model="selectedValues" mode="multiple"> <a-select-option v-for="(option, index) in options" :key="index" :value="option.value"> {{ option.label }} </a-select-option> </a-select> </div> </template> <script> export default { data() { return { options: [ { label: 'Option 1', value: 'option-1' }, { label: 'Option 2', value: 'option-2' }, { label: 'Option 3', value: 'option-3' }, { label: 'Option 4', value: 'option-4' }, ], selectedValues: [], isCheckAll: false, }; }, watch: { selectedValues() { const allValues = this.options.map((option) => option.value); this.isCheckAll = this.selectedValues.length === allValues.length; }, }, methods: { handleCheckAllChange(checked) { const allValues = this.options.map((option) => option.value); if (checked) { this.selectedValues = allValues; } else { this.selectedValues = []; } }, }, }; </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值