Angular 组件升级实战:将项目中的ng2-tree组件替换成ngx-tree

Posted Silam Lin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Angular 组件升级实战:将项目中的ng2-tree组件替换成ngx-tree相关的知识,希望对你有一定的参考价值。

Angular 树形组件ng2-tree与ngx-treeview


前阵子我做了一个比较好玩同时又是比较大坑的task。在这个过程中,踩了许多坑,走了一些奇奇怪怪崎岖的弯路hhhh。
不过实践下来终归是有趣好玩的,不仅学到了一些骚操作(尽管最后不一定采用,但确实是个好的思路),也加深了对这两个组件特性的理解。故对此过程做简单记录。

需求描述

需求:把项目中的ng2-tree升级成ngx-treeview

有人可能会问,为何?

那么,先了解一下什么是RC:

  • RC即Release Candidate, 是一个候选版本,不会再其上添加新功能,主要着重于排错。
  • 亦可了解一下GA,GA即General Availability, 是正式发布的版本。

可以在其npm上看到,ng2-tree:

ng2-tree正是一个rc版本,即是一个候选版本。对于想要构建一架牛哄哄的赛车,总不可能给赛车装一些“次品”,“候选品”吧??当然有条件的话,能用最好的就用最好的。

所以,才有要将ng2-tree这个“次品”,升级替换成ngx-treeview的需求!

初识ng2-tree

一个ng2-tree树的结构大概是这样子:


    value: 'Programming languages by programming paradigm',
    children: [
      
        value: 'Object-oriented programming',
        children: [
          value: 'Java',
          value: 'C++',
          value: 'C#'
        ]
      ,
      
        value: 'Prototype-based programming',
        children: [
          value: 'javascript',
          value: 'CoffeeScript',
          value: 'Lua'
        ]
      
    ]

每个节点都有value和children属性(可选)。value是该节点的值,children表示该节点是否有子节点,是个数组。

该树结构的父子/兄弟关系其实非常容易看出来。

接口:TreeModel Interface

interface TreeModel 
  value: string | RenamableNode;
  id: string | number;
  children?: Array<TreeModel>;
  loadChildren?: ChildrenLoadingFunction;
  settings?: TreeModelSettings;

结合一下TreeModel接口,树的结构大概是下面这样子:

public tree: TreeModel = 
    value: 'Programming languages by programming paradigm',
    id: 1,
    children: [
      
        value: 'Object-oriented programming',
        id: 2,
        children: [
          value: 'Java', id: 3,
          value: 'C++', id: 4,
          value: 'C#', id 5,
        ]
      ,
      
        value: 'Prototype-based programming',
        id: 6,
        children: [
          value: 'JavaScript', id: 7,
          value: 'CoffeeScript', id: 8,
          value: 'Lua', id: 9,
        ]
      
    ]
  ;

在我所做的那一Part里面,ng2-tree树结构节点的主要三个属性是

  • value
  • id
  • children

使用ng2-tree:

    <tree
      [tree]="tree"
      [settings]="settings"
      (nodeRemoved)="handleRemoved($event)"
      (nodeRenamed)="handleRenamed($event)"
      (nodeSelected)="handleSelected($event)"
      (nodeMoved)="handleMoved($event)"
      (nodeCreated)="handleCreated($event)"
      (nodeExpanded)="handleExpanded($event)"
      (nodeCollapsed)="handleCollapsed($event)"
      (loadNextLevel)="handleNextLevel($event)">
    </tree>

tree,settings,nodeSelected和nodeExpanded等这些,就是可以在ng2-tree的template模板里面使用的一些API吧。

demo实例:

<div class='ng2-tree'>
	<tree [tree]="treeNodes" [settings]="treeSettings" (nodeSelected)="showNgTwoTree($event)" (nodeExpanded)="onNodeExpanded($event)" (nodeCollapsed)="onNodeCollapsed($event)"></tree>
</div>

nodeSelected等这些的使用,具体看项目的功能需求。

初识ngx-treeview

其实ngx-treeview也和ng2-tree差不多,换汤不换药。使用上仅是在一些特性,功能,属性这些作了一些文章。

详细的介绍都可以去其npm上查看浏览。

先看看ngx-treeview的结构是怎么样:

const category = new TreeviewItem(
  text: "IT",
  value: 9,
  children: [
    
      text: "Programming",
      value: 91,
      children: [
        
          text: "Frontend",
          value: 911,
          children: [
             text: "Angular 1", value: 9111 ,
             text: "Angular 2", value: 9112 ,
             text: "ReactJS", value: 9113 ,
          ],
        ,
        
          text: "Backend",
          value: 912,
          children: [
             text: "C#", value: 9121 ,
             text: "Java", value: 9122 ,
             text: "Python", value: 9123, checked: false ,
          ],
        ,
      ],
    ,
    
      text: "Networking",
      value: 92,
      children: [
         text: "Internet", value: 921 ,
         text: "Security", value: 922 ,
      ],
    ,
  ],
);

每个node主要有三个属性

  • text
  • value
  • children

具体是什么就不多说了~

也可以单独创建一个节点,再把这个节点添加到某一节点的children数组里面:

const vegetableCategory = new TreeviewItem(
  text: "Vegetable",
  value: 2,
  children: [
     text: "Salad", value: 21 ,
     text: "Potato", value: 22 ,
  ],
);
vegetableCategory.children.push(
  new TreeviewItem( text: "Mushroom", value: 23, checked: false )
);

再看看treeviewItem都有些什么东西可以用:

demo示例:

config = TreeviewConfig.create(
	hasAllCheckBox:true,
	hasFilter:true,
	hasCollapseExpand:true,
	maxHeight:400;
)
<ngx-treeview class="ngx-tree" [items]='items' [config]="config" (selectedChange)="onSelectedChange($event)" (filterChange)="onFilterChange($event)"></ngx-treeview>

简单的使用就是这样子:

  • 数据放到items里;config是ngx-treeview的配置,可以自行配置其是否有checkbox,是否有过滤查找,最大高度是多少等等
  • 主要使用的属性,text,value,children,checked,collapsed和disabled。分别对应文本,值,子节点,是否被点击,是否展开,是否禁用。

改造之路 ①(坑)

如果要改造的话,就面临几个需要解决的问题:

  • 要把ngx-treeview的原生样式改得与原本的样式一样,需要统一。
  • ngx-treeview本质是个复选框checkbox。然而使用树形菜单,当然是要单选框radio。总不可能:点击nodeOne,响应nodeOne的内容;再点击nodeTwo,响应nodeOne和nodeTwo的内容吧!所以问题是怎么把checkbox改成radio.
  • ngx-treeview一开始Show出来后,每个node都是被选中的(总不能刚显示出菜单,就响应所有的内容吧!我压根都没有选过哪个菜单!)。所以要限制其一开始都是不选中的状态

修改样式

针对样式,我们可以通过F12,去看其已有样式是怎么样。再用 ::ng-deep 这些方法去做调整。

最终也是能把字体、颜色、间距padding和margin、以及小箭头这些的样式都改得一模一样。

所以这不是一个太大的问题。

限制一开始都是不选中

看treeviewItem的属性,可以看到,它有checked的访问器属性


原本checked都是true,我只要人为设置为false就好,这样就不会一开局就都选中了。

let node = new TreeviewItem(
	text:tress.value,value:trees.event,collapsed:true
);
node.checked = false;

这个是个setter,可不要写成:

node.checked(false);

那这个问题也好解决。

把checkbox改成radio?

这可能是最难弄的一个坑了…

复选框特性:

  • 点击one,则选中one;再点击one,one则被取消选中… 总不能,我点击菜单one,响应one的内容,再点击菜单one,就不响应one的内容了吧??
  • 点击one,则选中one;再点击two,则选中的有one和two…

输出其选中节点的数组,我曾以为:假如我选中了one,数组应该就是[one];再点击two,那数组变成[one,two]。即后选中的节点会添加到数组的后面。

那就:留下最后一个,删掉前面所有的节点不就好了吗?!这样请求的内容就是对应最后点击的节点。

但是,它并不是直接添加到数组中的最后一项,所以这个思路行不通,它是这样的:

点击One,数组是 [one]
点击Three,数组是[one,three]
点击Two,你以为数组会是[one,three,two]吗?其实是[one,two,three]…

那就不能只是简单地保留最后一项,删掉前面所有元素了。

再加上,哎,元素再点击可以被取消选中。。问题一下就变得不是太明朗。

查阅过一些资料:怎么把checkbox改成radio?

印象最深的答案:有radio为什么要用checkbox再把它改成radio。是单选就用单选啊,为什么要用复选再改成单选?

好像很有道理。我再去看看文档。

爬出坑

当我再看看文档和demo示例时,发现了确实有一个例子是单选框菜单。正合我意!!

所以对不熟悉的东西,还是要仔细看文档!!

以上是关于Angular 组件升级实战:将项目中的ng2-tree组件替换成ngx-tree的主要内容,如果未能解决你的问题,请参考以下文章

第1208期AngularJS 1.x平滑升级Angular实战

React实战之数据流方向与项目初始化

Angular项目实战Angular2的脏值检测机制

Angular Upgrade - 不能使用升级的组件作为入口组件

想要将项目从 Angular v5 升级到 Angular v6

WinForm通用自动更新器AutoUpdater项目实战