Antd Design 改造 TreeSelect


Antd Design 的 TreeSelect 的多選框功能,預設是父子關聯,當父選取時,自動將子全選;如果要單獨勾選父,可以將 treeCheckStrictly 屬性設定為 true,成為受控組件,解除父子關聯。

以下進行改造,將組件改造為以下使用:

  • 點選一次複選框,表示選中當前節點及該節點下的所有子節點
  • 點選第二次,表示選中當前節點 (取消子節點選取)
  • 點選第三次,表示不選中當前節點(取消父節點選取)

首先將 treeCheckStrictly 設定為 false,自行撰寫 handleChange 函式。

const data = {
  1: {
    id: 1,
    name: '爸爸',
    children: [2, 3]
  },
  2: {
    id: 2,
    name: '小孩1',
    children: []
  },
  3: {
    id: 3,
    name: '小孩3',
    children: []
  },
}


const getChildIdType = (
  children,
  selectIds,
  noSelectedChildId = [],
  allChildId = [],
) => {
  children.forEach((childId) => {
    const index = selectIds.findIndex((id) => id === childId);
    // 搜尋已選,若未選中,加入 noSelectedChildId
    index === -1 && noSelectedChildId.push(childId);

    allChildId.push(childId);
    if (
      data[childId].children &&
      data[childId].children.length > 0
    ) {
      // 還有下層節點
      getChildIdType(
        data[childId].children,
        selectIds,
        noSelectedChildId,
        allChildId,
      );
    }
  });
  return { noSelectedChildId, allChildId };
};

const handleChange = (value, label, extra) => {
  let selectIds = value.map((item) => item.value);
  if (
    extra.triggerValue &&
    data[extra.triggerValue].children.length > 0
  ) {
    // 不是子節點
    const { noSelectedChildId: noSelectedId, allChildId: allId } =
      getChildIdType(
        data[extra.triggerValue].children,
        selectIds,
      );
    if (extra.checked) {
      // 父節點被選中
      if (noSelectedId.length > 0) {
        // 子節點沒有全部選中,則全選子節點
        selectIds = [...selectIds, ...noSelectedId];
      }
    } else {
      // 父節點取消選取
      if (noSelectedId.length === 0) {
        // 若子節點全選中,則全部取消選取
        selectIds = value.filter((item) => !allId.includes(item.value));
        // 選取父節點
        selectIds.push(extra.triggerNode.props.value);
      }
    }
  }
  if (field && typeof field.onChange === 'function') {
    field.onChange(selectIds);
  }
};


參考資料:

  • antd design TreeSelect 改造
    Note. 文章中以 extra.triggerNode?.props?.children 來抓取小孩 node,個人在使用時不符合預期,所以改為直接抓 data 中的資料。讀者可再參考此篇文章做法。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *