我們在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中,則必須要透過TemplateRef與ContainViewRef兩個物件來做到,我們可以透過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。
沒有留言:
張貼留言