简体   繁体   中英

Filter treeNodes in Ant Design Tree

I'm using aSearchable Tree component in Ant Design. I would like to filter treeNodes on search. So that if a search value is found, a treeNode is shown. If not, it is hidden (ie filtered).

There is a filterTreeNode prop in API. Is it possible to filter treeNodes (hide unrelevant treeNodes from search) with this prop? If not, how can achieve the desired result?

Here is my code for filterTreeNode function:

const filterTreeNode = (node) => {
  const title = node.title.props.children[2];
  const result = node.key.indexOf(searchValue) !== -1 ? true : false
  console.log(searchValue);
  console.log(result);
  return result;
};

I can see that the result (true or false) is logged in the console, but with the Tree itself nothing happens.

Here is a link to codesandbox and the full code for the component:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Tree, Input } from "antd";
import gData from "./gData.js";

const { Search } = Input;

const dataList = [];
const generateList = (data) => {
  for (let i = 0; i < data.length; i++) {
    const node = data[i];
    const { key } = node;
    dataList.push({ key, title: key });
    if (node.children) {
      generateList(node.children);
    }
  }
};
generateList(gData);

const getParentKey = (key, tree) => {
  let parentKey;
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i];
    if (node.children) {
      if (node.children.some((item) => item.key === key)) {
        parentKey = node.key;
      } else if (getParentKey(key, node.children)) {
        parentKey = getParentKey(key, node.children);
      }
    }
  }
  return parentKey;
};

const SearchTree = () => {
  const [expandedKeys, setExpandedKeys] = useState([]);
  const [autoExpandParent, setAutoExpandParent] = useState(true);
  const [searchValue, setSearchValue] = useState("");

  const onExpand = (expandedKeys) => {
    setExpandedKeys(expandedKeys);
    setAutoExpandParent(false);
  };

  const onChange = (e) => {
    const { value } = e.target;
    const expandedKeys = dataList
      .map((item) => {
        if (item.title.indexOf(value) > -1) {
          return getParentKey(item.key, gData);
        }
        return null;
      })
      .filter((item, i, self) => item && self.indexOf(item) === i);
    if (value) {
      setExpandedKeys(expandedKeys);
      setSearchValue(value);
      setAutoExpandParent(true);
    } else {
      setExpandedKeys([]);
      setSearchValue("");
      setAutoExpandParent(false);
    }
  };

  const filterTreeNode = (node) => {
    const title = node.title.props.children[2];
    const result = title.indexOf(searchValue) !== -1 ? true : false;
    console.log(searchValue);
    console.log(result);
    return result;
  };

  const loop = (data) =>
    data.map((item) => {
      const index = item.title.indexOf(searchValue);
      const beforeStr = item.title.substr(0, index);
      const afterStr = item.title.substr(index + searchValue.length);
      const title =
        index > -1 ? (
          <span>
            {beforeStr}
            <span className="site-tree-search-value">{searchValue}</span>
            {afterStr}
          </span>
        ) : (
          <span>{item.title}</span>
        );
      if (item.children) {
        return { title, key: item.key, children: loop(item.children) };
      }

      return {
        title,
        key: item.key
      };
    });
  return (
    <div>
      <Search
        style={{ marginBottom: 8 }}
        placeholder="Search"
        onChange={onChange}
      />
      <Tree
        onExpand={onExpand}
        expandedKeys={expandedKeys}
        autoExpandParent={autoExpandParent}
        treeData={loop(gData)}
        filterTreeNode={filterTreeNode}
      />
    </div>
  );
};

ReactDOM.render(<SearchTree />, document.getElementById("container"));

As mentioned in the API document , filterTreeNode will highlight tree node, and will not hide it.

filterTreeNode

Defines a function to filter (highlight) treeNodes. When the function returns true, the corresponding treeNode will be highlighted

If you want to hide tree node, you will have to manually filter it first before before passing it to Tree in loop function, something like:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree, Input } from "antd";
import gData from "./gData.js";

const { Search } = Input;

const dataList = [];
const generateList = (data) => {
  for (let i = 0; i < data.length; i++) {
    const node = data[i];
    const { key } = node;
    dataList.push({ key, title: key });
    if (node.children) {
      generateList(node.children);
    }
  }
};
generateList(gData);

const getParentKey = (key, tree) => {
  let parentKey;
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i];
    if (node.children) {
      if (node.children.some((item) => item.key === key)) {
        parentKey = node.key;
      } else if (getParentKey(key, node.children)) {
        parentKey = getParentKey(key, node.children);
      }
    }
  }
  return parentKey;
};

const SearchTree = () => {
  const [expandedKeys, setExpandedKeys] = useState([]);
  const [autoExpandParent, setAutoExpandParent] = useState(true);
  const [searchValue, setSearchValue] = useState("");
  const [treeData, setTreeData] = useState(gData);

  const onExpand = (expandedKeys) => {
    setExpandedKeys(expandedKeys);
    setAutoExpandParent(false);
  };

  const onChange = (e) => {
    const value = e.target.value?.toLowerCase();
    const expandedKeys = dataList
      .map((item) => {
        if (item.title.indexOf(value) > -1) {
          return getParentKey(item.key, gData);
        }
        return null;
      })
      .filter((item, i, self) => item && self.indexOf(item) === i);
    if (value) {
      const hasSearchTerm = (n) => n.toLowerCase().indexOf(value) !== -1;
      const filterData = (arr) =>
        arr?.filter(
          (n) => hasSearchTerm(n.title) || filterData(n.children)?.length > 0
        );
      const filteredData = filterData(gData).map((n) => {
        return {
          ...n,
          children: filterData(n.children)
        };
      });

      setTreeData(filteredData);
      setExpandedKeys(expandedKeys);
      setSearchValue(value);
      setAutoExpandParent(true);
    } else {
      setTreeData(gData);
      setExpandedKeys([]);
      setSearchValue("");
      setAutoExpandParent(false);
    }
  };

  const filterTreeNode = (node) => {
    const title = node.title.props.children[2];
    const result = title.indexOf(searchValue) !== -1 ? true : false;
    console.log(searchValue);
    console.log(result);
    return result;
  };

  const loop = (data) => 
    data.map((item) => {
      const index = item.title.indexOf(searchValue);
      const beforeStr = item.title.substr(0, index);
      const afterStr = item.title.substr(index + searchValue.length);
      const title =
        index > -1 ? (
          <span>
            {beforeStr}
            <span className="site-tree-search-value">{searchValue}</span>
            {afterStr}
          </span>
        ) : (
          <span>{item.title}</span>
        );
      if (item.children) {
        return { title, key: item.key, children: loop(item.children) };
      }

      return {
        title,
        key: item.key
      };
    });

  return (
    <div>
      <Search
        style={{ marginBottom: 8 }}
        placeholder="Search"
        onChange={onChange}
      />
      <Tree
        onExpand={onExpand}
        expandedKeys={expandedKeys}
        autoExpandParent={autoExpandParent}
        treeData={loop(treeData)}
        filterTreeNode={filterTreeNode}
      />
    </div>
  );
};

ReactDOM.render(<SearchTree />, document.getElementById("container"));

Couple weeks ago, i had to meet same like this client's enhancement. So how i did it was, we had Search over DirectoryTree and inside tree we rendered the nodes with a function

 <DirectoryTree
              onExpand={handleOperatorExpand}
              expandedKeys={expandedKeys}
              autoExpandParent={autoExpandParent}
              onSelect={handleOperatorSelect}
            >
              {renderTreeNodes(mtoCharts)}
            </DirectoryTree>

In search onChange event we saved values to a state, like searchValue. Then searchValue was checked in renderTreeNodes function with regex:

let dynamicRegex = `.*(${searchValue.toUpperCase()}){1}.*`;

if

item.name.match(dynamicRegex)

then we display the node otherwise we dont. SIMPLE !

UPDATE : codes

<Search onChange={handleOperatorChange} />

const handleOperatorChange = e => {
const { value } = e.target;
let temp = value.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()"'+@<>?\\\[\]]/g, '');

setSearchValue(temp);
setAutoExpandParent(true);};

and finally renderTreeNodes :

const renderTreeNodes = data =>
data &&
data instanceof Array &&
data.map(item => {
  const title = (
    <Highlighter
      highlightStyle={{ color: '#f50' }}
      searchWords={[searchValue]}
      autoEscape={true}
      textToHighlight={item.name}
    />
  );

  if (searchValue) {
    let dynamicRegex = `.*(${searchValue.toUpperCase()}){1}.*`;
    if (!isEmpty(item.children)) {
      // let doesChildrenHaveMatchingName = false;
      // item.children.forEach(itemChild => {
      //   if (itemChild.name.match(dynamicRegex)) {
      //     doesChildrenHaveMatchingName = true;
      //   }
      // });
      // if (doesChildrenHaveMatchingName) {
      return (
        <TreeNode
          className={!item.active ? 'dim-category' : ''}
          key={item.code}
          title={title}
          id={item.id}
          code={item.code}
          level={item.level}
          parentId={item.parentId}
          parentReference={item.path}
          icon={({ expanded }) => (
            <Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
          )}
        >
          {renderTreeNodes(item.children)}
        </TreeNode>
      );
      // }
    }

    if (item.name.match(dynamicRegex) || item.path.match(dynamicRegex)) {
      return (
        <TreeNode
          className={item.active ? 'false' : 'dim-category'}
          key={item.code}
          title={!item.leaf ? title : getMtoTitle(item, title)}
          {...(item.leaf ? { leaf: item.leaf } : {})}
          id={item.id}
          code={item.code}
          level={item.level}
          parentId={item.parentId}
          parentReference={item.path}
          icon={({ expanded }) => {
            return item.leaf ? (
              <Icon type="money-collect" style={{ color: '#47bfa5' }} />
            ) : (
              <Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
            );
          }}
        />
      );
    }
  } else {
    if (!isEmpty(item.children)) {
      return (
        <TreeNode
          className={!item.active ? 'dim-category' : ''}
          key={item.code}
          title={title}
          id={item.id}
          code={item.code}
          level={item.level}
          parentId={item.parentId}
          parentReference={item.path}
          icon={({ expanded }) => (
            <Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
          )}
        >
          {renderTreeNodes(item.children)}
        </TreeNode>
      );
    }

    return (
      <TreeNode
        className={item.active ? 'false' : 'dim-category'}
        key={item.code}
        title={!item.leaf ? title : getMtoTitle(item, title)}
        {...(item.leaf ? { leaf: item.leaf } : {})}
        id={item.id}
        code={item.code}
        level={item.level}
        parentId={item.parentId}
        parentReference={item.path}
        icon={({ expanded }) => {
          return item.leaf ? (
            <Icon type="money-collect" style={{ color: '#47bfa5' }} />
          ) : (
            <Icon type={expanded ? 'folder-open' : 'folder'} style={{ color: '#32327f' }} />
          );
        }}
      />
    );
  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM