angular動態表單

這邊文章主要介紹angular動態表單的實現思路。具體實現細節能夠參考社區裏semlinker的 動態建立表單這篇文章,以及他推薦的參考資源 Configurable Reactive Forms in Angular with dynamic components,筆者這篇文章主要是以上文章的部分翻譯和思考。
查看demo源碼
查看demo演示

動態表單使用場景

有時候咱們須要一個靈活的表單,這個表單能夠根據用戶的選擇,或者服務器返回的信息進行從新配置,好比:增長或刪除一組input元素、一組select元素,等等。git

在這樣的狀況下,若是一開始就在模板裏寫下全部的表單,利用一個ngif樹狀結構進行選擇控制,程序會變得比較冗餘。github

這時。程序最好是可以根據用戶的選擇(driven by configuration)或者服務器的響應,自動生成所須要的表單。這就是動態表單要處理的業務。segmentfault


組件生成的相關概念

  • 組件的兩個構成

要動態生成表單,須要先理解組件是如何生成的。服務器

一個angular組件由兩部分所組成。app

  1. Wrapper

Wrapper可以與組件進行交互,當一個Wrapper初始化完成後,就已經幫咱們實例化了一個組件。同時,它也負責組件的change detection,以及觸發鉤子函數好比ngOnInit,ngOnChanges。dom

  1. View

View負責呈現渲染事後的模板,將組件的外貌展現出來,而且可以觸發Wrapper的change detection。一個組件能夠有多個view,每個view能夠經過調用angular提供的兩個函數自行生成和銷燬,這個過程不用頂層的視圖參與。函數

  • 組件的一般加載方式(非動態加載方式)

一般狀況下,咱們都是把組件內嵌到根組件或者另外一個組件當中使用。嵌入的組件稱爲子組件,被嵌入的稱爲父組件。這時,當咱們的子組件代碼在被編譯時,會生成一個組件工廠component factory(這是angular核心類ComponentFactory的一個實例),和一個hsot view,host view負責本組件在父組件視圖內生成該組件的dom節點,以及生成該組件的wrapper和view。spa

  • 動態加載組件

而當咱們想要將一個動態組件插入某個組件視圖時,則沒法取得這個動態組件的實例,由於這些是非動態組件編譯器作的事。翻譯


實現動態組件

angular提供了一些函數解決上面的難題,要使用這些函數咱們須要注入兩個對象。code

constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private viewcontainerRef: ViewContainerRef,
  ) {
      
  }

咱們注入了ComponentFactoryResolver,和ViewContainerRef。

ComponentFactoryResolver上提供了一個方法(resolveComponentFactory()),該方法接收一個組件類做爲參數,生成一個基於該組件類的組件工廠,也就是咱們以前提到的那個組件工廠。

ViewContainerRef提供了一個方法(createComponent()),該方法接收組件工廠做爲參數,在該視圖中生成子組件。(我我的的理解是它處理了host view所作的事,爲組件生成了wrapper和view)


實現動態表單

上文簡要的介紹了實現動態組件的一些技術,如今開始思考如何作一個動態表單。

  • 具體思路

咱們想要作出一個獨立的動態表單模塊,當咱們想要使用動態表單時,只需簡單引入這個模塊,稍加配置便可使用。

咱們但願這個模塊作好了後,在頂層使用者的角度會是這樣一個工做流程:

咱們能夠很容易的作出一個具備輸入屬性的組件,問題的核心在於這個組件是如何根據輸入屬性生成咱們想要的表單。

也就是說,是它本身調用ComponentFactoryResolver和ViewContainerRef進行組件的動態生成,仍是交給別人處理。

下圖是實現思路:

  1. 實際上咱們把動態表單拆分紅了一個個小的動態組件(不預先加載),由外層的一個組件充當一個容器,全部的動態組件都會在裏面進行生成和銷燬,他們共同組成了一個動態表單。
  2. 調用ComponentFactoryResolver和ViewContainerRef生成組件的的這部分邏輯沒有集成在外層容器中,而是交給了一個自定義的指令和ng-container。由於指令沒有視圖,他經過注入ViewContainerRef獲取到的是宿主的視圖容器。因爲ng-container不會被渲染,因此獲取到的視圖容器就是外層組件容器的視圖容器。

這麼處理的好處就是不須要由外層組件統一對各個拆分的動態組件進行管理,至關因而由動態組件本身進行管理。

外層組件容器大概會是下面這樣:

<form>
  <ng-container *ngFor="let config of configs" [自定義指令] >
 </ng-container>
</form>
configs是用戶的配置數據,自定義指令寄宿在ng-container中,根據config渲染出各自的動態組件,而ng-container是透明的。

看一下代碼目錄結構,最後會是這個樣子

以上就是大致的實現思路了,具體還有許多細節能夠關注文章開頭提到的那兩篇文章,講的很詳細。
相關文章
相關標籤/搜索