Angular2+ 自定義Tree元件(參考Angular-material的CdkTree)

初之發表於2019-04-13

為了方便, 本文所有的Angular均表示Angula2及以上版本,對於使用Angular1/Angular.js的讀者可作為參考

前言

目前正在寫一個AngularMarkdown編輯器,需要一個樹形元件來寫檔案系統。剛好之前用過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 MaterialCdkTree。他的結構如下

<tree dataSource="ds">
    <tree-node>
        <!--本節點內容-->
        {{ nodeName }}
        <!--子節點出口-->
        <outlet></outlet>
    </tree-node>
</tree>
複製程式碼

說明: 簡單的說就是CdkTree<tree-node>裡的內容作為一個模板儲存起來,然後根據資料來源遞迴渲染出來。這樣我們就可以在不修改TreeTreeNode元件前提下改變其內容。

實現

在實現之前需要理解Angular的幾個裝飾器,學過Angular的應該都不會陌生。

  • @ViewChild - 在檢視中查詢匹配的第一個元素
  • @ViewChildren - 在檢視中查詢匹配的所有元素
  • @ContentChild - 在元件標籤包裹的內容中查詢匹配的第一個元素
  • @ContentChildren - 在元件標籤包裹的內容中查詢匹配的所有元素

View和Content的區別
View: 在元件的模板中定義的內容,即我們手動寫在xxx.component.html裡的內容 Content: 在Host元素的<opening><closeing>標籤中的內容

概覽

在Tree元件中有四個比較重要的類

  • @Component: TreeComponent
  • @Component: TreeNodeComponent
  • @Directive: TreeNodeOutletDirective
  • @Directive: TreeNodeDefDirective

TreeComponent

該元件就是我們要是實現的Tree元件,用於包裹TreeNode

TreeNodeComponent

樹節點元件,我們自定義的模板就寫在這裡面

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元件連結

相關文章