kendo-react UI库踩坑(treeview组件)

使用Help-function时需要注意,当你完全采用Kendo UI库推荐的方法时,会遇到一个问题

下面是完全按照Kendo推荐的方法写的

当用户选择1A 和1D

打印check

你会看到ids中包含选中项序列号( 序列号"0_0"对应内容1A, "0_2" 对应内容1D )

如果这个列表的内容一直固定,则没有问题,因为你的序列号和内容始终对应,  如“0_0” 对应1A

但是当列表内容是动态的,则不能这么做,因为序列号和内容不一定匹配

比如,当列表是动态的,这个时候选择第四项,数据的渲染是根据ids的内容来匹配的,

此时选中的ids为("0_0",  "0_2", "0_3")  预想选中的内容是1A, 1D, 2B,

但是实际上

你选中的内容是1B, 2A, 2B(  因为新的列表中 "0_0" 对应的是1B,  "0_2" 对应的是2A, "0_3"对应的是2B )

 

 

结论

当列表内容为固定值,则可以完全按照Kendo推荐的方法去实现,最终选中项从ids中获取

当列表内容是动态的,则最终结果不能仅仅从ids中获取

 

 

那么当列表内容是动态的,想获取最终结果怎么做呢? 

思路:  充分利用ids,  列表用一个数组表示, 数组中每一项都有一个唯一下标和一个check属性,

如果这项被选中(check为true ),则ids中能找到对应下标,如果没被选中, ids中不存在对应下标。

每次请求数据后,新的列表就会覆盖旧列表, 在覆盖之前根据ids和旧的列表,就可以获取到上一次选中的内容。

 

举个栗子,因为我做的列表有两层机构, 所以数组结构为
 

[
        {
          text: "Machine Name",
          expanded: false,
          items: [
            {text: "HotEye-L12-1"},
            {text: "HotForm-L12-2"},
            {text: "HotMass-L12-3"},
            {text: "HotSystem-L12-3"},
            {text: "MCAL-L12-2"},
            {text: "Multi_L13"},
            {text: "Mx4_L11"}
          ],
        },
]

所以当选中父项时,ids显示为"0" , 选中子项时,

例如,选中HotForm-L12-2,它在数组中的下标为 1,  所以ids为"0_1"

       选中MCAL-L12-2,它在数组中的下标为 4,  所以ids为"0_4"

 

第一次列表有七项,假设选中HotEye-L12-1,MCAL-L12-2,  点击页面其他筛选条件,发送请求,获取到新的machine Name列表

新列表中只有MCAL-L12-2

要求:  显示并选中HotEye-L12-1,MCAL-L12-2

最终结果:

重要分析部分  获取到新列表然后渲染之前,找到旧的ids, 根据ids和旧的列表找到选中项HotEye-L12-1,MCAL-L12-2, 判断新列表中是否存在这两项,然后更新列表和ids.

 

部分代码

子组件,采用Kendo treeView

  FilterTree.tsx

import * as React from "react";
import {
  TreeViewCheckChangeEvent,
  TreeViewExpandChangeEvent,
  TreeView,
  processTreeViewItems,
  TreeViewOperationDescriptors,
  TreeViewCheckChangeSettings,
  handleTreeViewCheckChange,
  TreeViewCheckDescriptor,
} from "@progress/kendo-react-treeview";
import {IFilterTreeItem} from "./IFilterTreeItem";


export interface IFilterTreeProps<T> {
  /** filter list */
  filterList: IFilterTreeItem<T>[];

  /** used to handle expand/checked property of filter list */
  filterOperations: TreeViewOperationDescriptors;

  /** triggered this function when user checked or un-checked items */
  onCheckChange: (newOperetions: TreeViewOperationDescriptors) => void;

  /** triggered this function when user expanded or collapsed items */
  onExpandChange: (newOperetions: TreeViewOperationDescriptors) => void;
}

export class FilterTree<T> extends React.Component<IFilterTreeProps<T>> {
  constructor(props: IFilterTreeProps<T>) {
    super(props);
    this.filterUpdate = this.filterUpdate.bind(this);
    this.filterExpand = this.filterExpand.bind(this);
  }

  public render() {
    const {filterList, filterOperations} = this.props;

    /**
     * use kendo component function to handle data:[processTreeViewItems
     * ](https://www.telerik.com/kendo-react-ui/components/treeview/api/processTreeViewItems/)
     */
    const data = processTreeViewItems(filterList, filterOperations) as IFilterTreeItem<string>[];
    return (
      <div className="tes-components-mainFilter-filterTree">
        <TreeView
          className="tes-components-Gallery-filter-kendoUI"
          data={data}
          checkboxes={true}
          expandIcons={true}
          onCheckChange={this.filterUpdate}
          onExpandChange={this.filterExpand}
        />
      </div>
    );
  }

  /**
   * toggle when user change filter item
   * @param event `TreeViewCheckChangeEvent` user selected item
   * * Note: I use kendo component , you can see [Help function
   * ](https://www.telerik.com/kendo-react-ui/components/treeview/checkboxes/helper-functions/)
   */
  private filterUpdate(event: TreeViewCheckChangeEvent) {
    const {filterList, filterOperations} = this.props;
    const settings: TreeViewCheckChangeSettings = {checkChildren: true, checkParents: true};
    const check = handleTreeViewCheckChange(
      event,
      filterOperations.check as string[] | TreeViewCheckDescriptor,
      filterList,
      settings
    ) as string[] | TreeViewCheckDescriptor;
    const newOperetions: TreeViewOperationDescriptors = {
      ...filterOperations,
      check,
    };
    this.props.onCheckChange(newOperetions);
  }

  /**
   * toggle when user expanded or collapsed filter
   * @param event `TreeViewCheckChangeEvent`  current filter item
   */
  private filterExpand(event: TreeViewExpandChangeEvent) {
    const {filterOperations} = this.props;
    const expand = (filterOperations.expand as string[]).slice();
    const index = expand.indexOf(event.itemHierarchicalIndex);
    if (index === -1) {
      expand.push(event.itemHierarchicalIndex);
    } else {
      expand.splice(index, 1);
    }
    const newOperetions: TreeViewOperationDescriptors = {
      ...filterOperations,
      expand,
    };
    this.props.onExpandChange(newOperetions);
  }
}

IFilterTreeItem.ts

export interface IFilterTreeItem<T> {
  /** Displayed label associated with the filterItem */
  text: T;

  /**
   * expanded or collapsed items.
   */
  expanded?: boolean;

  /** children of the item */
  items?: IFilterTreeItem<T>[];
}

 

父组件

state


  /** filter list for machine name */
  machineNameFilterTree: IFilterTreeItem<string>[];

  /** used to handle expand/checked property of machineName filter list
   * * more detail you can see [kendo help function
   * ](https://www.telerik.com/kendo-react-ui/components/treeview/checkboxes/helper-functions/)
   */
  machineNameOperations: TreeViewOperationDescriptors;
  constructor(props) {
    super(props);
    this.state = {
       machineNameFilterTree: [],
       machineNameOperations: {
        expand: [],
        check: {ids: [], applyCheckIndeterminate: true},
      },
    }
  }

componentDidmount

 public componentDidMount() {
    this.state.logger.debug("componentDidMount start");
//filters.machineNameFilters就是后端传回的新列表
    const {machineNameFilterTree, machineNameOperations} = this.changeFormatOfMachineName(filters.machineNameFilters);
    this.setState({
      machineNameFilterTree,
      machineNameOperations,
    });
    this.state.logger.debug("componentDidMount end");
  }

componentDidUpdate


  public componentDidUpdate(prevProps: IMainFilterProps) {
    this.state.logger.debug("componentDidUpdate start");
//filters是后端传回的列表(请根据你自己的代码来修改此值)
    const newFilterList = this.props.filters;
    if (JSON.stringify(prevProps.filters) !== JSON.stringify(newFilterList)) {

      const {machineNameFilterTree, machineNameOperations} = this.changeFormatOfMachineName(
        newFilterList.machineNameFilters
      );
      this.setState({
        machineNameFilterTree,
        machineNameOperations,
      });
    }
    this.state.logger.debug("componentDidUpdate end");
  }

渲染子组件


  /** render machine name filter */
  private machineNameFilterRender() {
    const { machineNameFilterTree, machineNameOperations} = this.state;
    return machineNameFilterTree && machineNameFilterTree.length !== 0 ? (
      <FilterTree
        filterList={machineNameFilterTree}
        filterOperations={machineNameOperations}
        onCheckChange={this.machineNameChange}
        onExpandChange={this.machineNameExpand}
      />
    ) : null;
  }

machineNameExpand

  /**
   * toggle when user expanded or collapsed machineName filter
   * @param newOperetions `TreeViewOperationDescriptors` machineName operations
   * *change state 'machineNameOperations'
   */
  private machineNameExpand(newOperetions: TreeViewOperationDescriptors) {
    this.setState({machineNameOperations: newOperetions});
  }

machineNameChange


  /**
   * toggle when user change machineName filter
   * @param newOperetions `TreeViewOperationDescriptors` machineName operations
   * *change state 'machineNameOperations'
   */
  private machineNameChange(newOperetions: TreeViewOperationDescriptors) {
      this.setState({machineNameOperations: newOperetions}, () => {
//这里根据你自己的代码来写,就是获取到选中的machineName,然后发送请求
        this.notifyFilterChange();
      });
  }


//下面是在我的情境中获取选中的machineName方法 ,主要是getSelectedMachineNames

 private getSelectedItemsArr(operations: TreeViewOperationDescriptors, filterTree: IFilterTreeItem<string>[]) {
    if (
      operations.check !== undefined &&
      "ids" in operations.check &&
      operations.check.ids &&
      operations.check.ids.length !== 0
    ) {
      // 当length为1,说明你选中了所有项
      const allSub = (operations.check.ids as string[]).filter((i) => i.length === 1);
      if (allSub.length !== 0 && filterTree[0].items) {
        return filterTree[0].items.map((e) => e.text);
      } else {
        const selectedList: string[] = [];
        // geted selected item's subscript then compared with 'filterTree' to find item
        operations.check.ids.forEach((e: string) => {
          const selectedIndexArr = e.split("_");
          // 否则,就是选中了部分项
          if (selectedIndexArr.length !== 1 && filterTree[0].items) {
            const sub = Number(selectedIndexArr[1]);
            // 找到你选中的其中之一
            const selectedItem = filterTree[0].items[sub].text;
        
            selectedList.push(selectedItem);
          }
        });
        return selectedList;
      }
    }
    return [];
  }

  /** get user selected machineName filter item
   * @param machineNameOperations `TreeViewOperationDescriptors` machine name operation
   */
  private getSelectedMachineNames(machineNameOperations: TreeViewOperationDescriptors) {
    const {machineNameFilterTree} = this.state;
    const selectedList = this.getSelectedItemsArr(machineNameOperations, machineNameFilterTree);
    return selectedList;
  }

changeFormatOfMachineName(重要部分)


  /** change format of machineName filter list
   * @param machineNameFilterList `string[]` machineName filter list of props.filters
   */
  private changeFormatOfMachineName(machineNameFilterList: string[]) {
    let formatedList: IFilterTreeItem<string>[] = [];
    let newMachineNames: string[] = [];
    let ids: string[] = [];
//这个方法是处理旧的列表和ids, 然后得到新列表和新的ids
    const {newList, newIds} = this.getNewMachineNames(machineNameFilterList);
    newMachineNames = newList;
    ids = newIds;

    // when selected all child item , father item will be selected
    if (ids.length !== 0 && newMachineNames.length !== 0 && ids.length === newMachineNames.length) {
      const sub = ids.indexOf("0");
      if (sub === -1) ids.push("0");
    }

//这里是修改列表的数据格式
    if (newList.length !== 0) {
      // first level of machineName filter
      formatedList = [
        {
          text: "MachineName",
          expanded: false,
          items: [],
        },
      ];
      newMachineNames.forEach((e) => {
        // second level of machineName filter
        (formatedList[0].items as IFilterTreeItem<string>[]).push({text: e});
      });
    }

    const machineNameOperations = {...this.state.machineNameOperations, check: {ids, applyCheckIndeterminate: true}};
    return {machineNameFilterTree: formatedList, machineNameOperations};
  }

这是getNewList


  /** 得到新的列表和新的ids
   * @param filterList `string[]` machineName filter list of props filters
   */
  private getNewMachineNames(filterList: string[]) {
    let newIds: string[] = [];
    let newList: string[] = filterList;
    // 这里就是前面展示的获取选中项的方法,在前面可以找到
    const currentSelectedItems = this.getSelectedMachineNames(this.state.machineNameOperations);
    if (currentSelectedItems.length !== 0) {
      /** if didn't find selected items in new list,  add these items in new list */
      newList = this.getNewList(newList, currentSelectedItems);
      newList.sort();
      // used to get new check operations
      newIds = this.checkedItems(newList, currentSelectedItems);
    }
    return {newList, newIds};
  }



  /** 当你在新列表中找不到刚才选中项,那么把刚才选中项加入新列表
   * @param newItemArr `T[]` new list of filter
   * @param selectedItemArr `T[]` selected list of filters item
   */
  private getNewList<T>(newItemArr: T[], selectedItemArr: T[]): T[] {
    const newList = JSON.parse(JSON.stringify(newItemArr)) as T[];
    selectedItemArr.forEach((e) => {
      const item = newItemArr.filter((i) => i === e);
      if (item.length === 0) {
        newList.push(e);
      }
    });
    return newList;
  }

  /** 修改ids
   * @param newItemArr `T[]` new list of filter
   * @param selectedItemArr `T[]` selected list of filters item
   */
  private checkedItems<T>(newItemArr: T[], selectedItemArr: T[]): string[] {
    const ids: string[] = [];
    newItemArr.forEach((e, index) => {
      selectedItemArr.forEach((i) => {
        // if item still exist in new list, keep it as selected
        if (e === i) {
          ids.push(`0_${index}`);
        }
      });
    });
    return ids;
  }

 

大部分代码在这里,阅读起来可能比较困难,建议先阅读Help-function,

具体细节请根据自己的情景加以修改

以上是我的理解,欢迎大佬指教

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值