为了方便, 本文所有的Angular均表示Angula2及以上版本,对于使用Angular1/Angular.js的读者可作为参考前言目前正在写一个Angular的Markdown编辑器,需要一个树形组件来写文件系统。刚好之前用过Angular Material的树形组件,就想照着写一个。毕竟直接把Angular Material引进来会多出其他用不到的组件,而且自己实现还能更深入学习Angular。介绍在写之前我百度了一通,发现大部分文章的Tree组件实现都是把节点模板直接写在了组件里,类似下面<!–Tree组件–><tree> <tree-node></tree-node></tree><!–TreeNode组件–><tree-node> <!–本节点内容–> {{ nodeName }} <!–子节点–> <tree-node *ngIf=“hasChildren”> </tree-node><tree-node>说明一下: Tree组件里面包含TreeNode组件,TreeNode组件内部实现了递归子节点的逻辑。 其实这样的结构已经足够满足我的需求了,但是(因为强迫症)这样的可重用性几乎是没有,因为节点的内容已经写死在TreeNode组件里了。 然后我想到了Angular Material的CdkTree。他的结构如下<tree dataSource=“ds”> <tree-node> <!–本节点内容–> {{ nodeName }} <!–子节点出口–> <outlet></outlet> </tree-node></tree>说明: 简单的说就是CdkTree把<tree-node>里的内容作为一个模板保存起来,然后根据数据源递归渲染出来。这样我们就可以在不修改Tree和TreeNode组件前提下改变其内容。实现在实现之前需要理解Angular的几个装饰器,学过Angular的应该都不会陌生。@ViewChild - 在视图中查询匹配的第一个元素@ViewChildren - 在视图中查询匹配的所有元素@ContentChild - 在组件标签包裹的内容中查询匹配的第一个元素@ContentChildren - 在组件标签包裹的内容中查询匹配的所有元素View和Content的区别 View: 在组件的模板中定义的内容,即我们手动写在xxx.component.html里的内容Content: 在Host元素的<opening>和<closeing>标签中的内容概览在Tree组件中有四个比较重要的类@Component: TreeComponent@Component: TreeNodeComponent@Directive: TreeNodeOutletDirective@Directive: TreeNodeDefDirectiveTreeComponent该组件就是我们要是实现的Tree组件,用于包裹TreeNodeTreeNodeComponent树节点组件,我们自定义的模板就写在这里面TreeNodeOutletDirective这个指令设置了子节点的出口位置TreeNodeDefDirective这个指令用来定义树节点所需的数据,即我们使用这个指令让模板可以使用每个树节点对应的数据实现我们先看一下完成后的样子<nb-tree [dataSource]=“fileTree”> <nb-tree-node *nbTreeNodeDef=“let data = data”> <li> <span>{{ data.title }}</span> </li> <ul> <ng-container nbTreeNodeOutlet></ng-container> </ul> </nb-tree-node></nb-tree>(标签前面的nb请忽略,这只是默认的前缀)上面是完成后的简易版。我们可以看到在tree组件上设置了dataSource。然后在treeNodeDef指令中我们导出了数据对象data。然后在模板中使用了它<span>{{ data.title }}</span>。最后我们在<ng-container>上用treeNodeOutlet指令设置了子节点的出口。懒得详细写实习了。。。有空再写吧。本文主要提供一个通用树形组件的思路。想看代码的,看结尾。有一个不是很完善的tree组件,我用在正在写的Markdown编辑器上了。结尾Github: tree组件链接