Angular 4: Structural Directive


我們在Angular內可以自己定義Directive,而可以對DOM作新增、移除或是修改Element的Directive又稱作Structural Directive。

像是ngIf或是ngFor這一類的directive就是structural directive,ngIf可以在特定條件下讓element加入或移除DOM;nfFor則是根據資料來源將element加入或移除DOM中。



通常structural directive前面都會跟著*(會依據使用方式的不同,有的並不用放上 *),表示是structural directive,而同一個element上是不能同時存在兩個或以上的structural directive的。

除了structural directive外,另一種directive稱作attribute directive,與structive directive不同的是,attribute directive主要是用來改變element的外觀,像是style,而且attribute directive不會限定一個element上只能使用一個,element上可以有許多的attribute element。


在Angular 2之後,structural directive前會有 *,其實這是Angular的語法糖,他會將structural directive作轉換:

<p *ngIf="true">Show me.</p>


轉換為

<p template="ngIf = true">Show me.</p>


最後變成

<ng-template  [ngIf]="true">

     <p>Show me.</p>

</ng-template>

可以發現到,ngIf變成了ng-template上的property binding
所以實際上,使用ngIf這類的directive,就是在做這樣的轉換,這就是為什麼要在structural directive前加入 * 的原因, ngFor與ngSwitch的原理都是一樣的。


但在某些特殊情況下,轉換成ngTemplate方式就不適用了,官方文件提到,有些HTML Tag後面會緊接著特定的element tag,像是 select與option,如果我們希望讓option透過像是ngIf的方式來決定是否要顯現,做了之後會發現select 選單變成空白的,可參考下面程式結果:



透過這種方式會無法正常顯示select選單,這種特殊情況就要透過ngContainer來調整了。

參考官網對ngContainer的解釋:
The Angular <ng-container> is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM
可以知道ngContainer不會被放在DOM中,也不會受到style的影響,因此條件式顯示select option的程式可以透過ngContainer改寫成:




客製化的structural directive

如果要自己寫一個structural directive,如同前面提到的,程式會將structural directive轉換成ngTemplate再放入DOM中,則必須要透過TemplateRefContainViewRef兩個物件來做到,我們可以透過TemplateRef存取ngTemplate的內容;透過ContainerViewRef指存取ngTemplate的Container。

今天要寫一個 unless的 directive,先透過angular-cli建立一個unless的directive

我們希望unless使用方式如下:
<div *appUnless="condition">
Show something
</div>

1. constructor傳入TemplateRef與ContainerViewRef
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef) {}

2. 加入綁定condition的變數

這變數通名稱常跟directive的sekector一樣( 我目前觀察是這樣),透過Input()允許讓template的地方做data binding,特別的是,變數前面要加入 set(概念類似setter function)表示每當變數有變動時,就執行function。

  @Input() set appUnless(condition: boolean) {

    if (!condition) {

      this.viewContainerRef.createEmbeddedView(this.templateRef);

    } else {

      this.viewContainerRef.clear();

    }

  }
這邊可以看到,每當條件作not的結果為true時,就會將templateRef的內容加入containerViewRef;相反的,如果運算結果為false,則從ContainerViewRef中移除。

基本上到這邊就完成了,之後就可以透過該方式寫自己的structural directive,條件可以更客製化。

程式可以參考下面程式碼,當數字為偶數時才顯示文字,基數時隱藏,使用方式跟ngIf一樣,只是邏輯相反。



更詳細的Structural Directive說明,可參考Angular網站:STRUCTURAL DIRECTIVES


沒有留言:

張貼留言

Java Spring Framework 筆記 - Autowiring (2)

這篇記錄透過 Annotation來做到 Spring的 autowiring。