Angular 组件升级实战:将项目中的ng2-tree组件替换成ngx-tree
Posted Kabukiyo 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实战
Angular Upgrade - 不能使用升级的组件作为入口组件