前言
總算到介紹到最後一個 Angular 的指令 - 結構型指令 (Structural Directives) 啦,打起精神繼續往下學習吧!
結構型指令 (Structural Directives)
這種結構型的指令會透過新增或刪除 DOM 的元素動態改變 DOM 的結構,內建有三種語法:
- ngIf
- ngSwitch
- ngFor
相信看到這些語法大概也猜得出七八成是在做什麼的,以下一一示範如何使用。
ngIf
ngIf 的用法相當簡單,可以透過這個指令動態的新增或者是刪除一整段 HTML 的內容。
假設這個 ICON 區塊會根據 Counter 的數字動態的顯示或隱藏,那該怎麼做呢?
首先找到 Template 內對應的 HTML 區塊,並進行修改
1 | <div id="social-icons" class="pull-right social-icon" *ngIf="counter % 2 == 0"> |
特別的是 ngIf 前面必須加上一個 * 號,這個星號是結構型指令專屬的語法,算是語法糖的一種。
只要是結構型指令,記得加上 * 號就對了。,不過我們也不需要真的死背,可以善用 code snippet 來輔助。
而 *ngIf
的等號後方同樣也是接布林值,因此在這裡我們填入 counter % 2 == 0
這樣就可以了。
- 只要回傳 True ,那麼就新增這個 HTML 區塊,反之則刪除
測試看看吧!
這邊要強調的是 ngIf 是真的操作 DOM 元素動態改變結構進行新增或是刪除,也就是當回傳 False 時該區塊是完全不存在網頁中的,並非單純的隱藏。
需要注意的地方
使用 ngif 時,雖然語法相當的簡單,可是有生命週期的細節需要注意。
使用 ngif 新增或刪除某個區塊的 HTML 時,那些區塊可能含有其他的 Component
- 當
*ngif = false
時 Component 會一起被刪除 - 當
*ngif = True
時 Component 被新增回來
也就是說這樣的情況下會影響到 Component 的生命週期,當 Component 被刪除時生命週期也就跟著結束了。
因此下一次新增回來的 Component 狀態肯定會跟之前的不同,這點需要特別注意
這個 Component 狀態好比 HeaderComponent 內的 counter ,假如目前
counter = 5
當元件被刪除又新增後,這個時候的counter
已經被重置了。
ngSwitch
ngSwitch 也是個結構型指令,假設我們有個需求是這樣:
- 根據 ngSwitch 去切換前三個 ICON 與後三個 ICON 的內容
- 切換的條件根據
counter % 2
產生的不同的餘數來切換
首先找到 Template 內對應的 HTML 區塊,並進行修改
1 | <div id="social-icons" class="pull-right social-icon"> |
[ngSwitch]
後面接的是條件,在這裡我們的條件設定為 counter % 2
,而 *ngSwitchCase
後面接的是「當遇到 0 或 1 時要顯示哪些東西」,那如果都不符合的話的則顯示 *ngSwitchDefault
的結果。
存檔測試看看吧!
成功是成功了,但這時會發現使用 ngSwitch 導致 HTML 結構不一致。
使用 ngSwitch 導致 HTML 結構不一致
這該怎麼修正呢?
可以使用
ng-container
來取代使用 ngSwitch 時產生的 div 標籤
1 | <div id="social-icons" class="pull-right social-icon"> |
再次觀察 DOM 結構
此時就沒有多餘的 DIV 標籤了。
從這邊我們知道,當使用結構型指令時可以使用 ng-container 標籤,使其不會產生出額外的 HTML 標籤。
ngFor
ngFor 的功能相當強大,使用的時機也相當頻繁。
舉例來說,當我們串接 API 的時候,會取得一份資料。此時可能會透過迴圈的方式顯示在畫面上,這時後就有機會用上 ngFor 。
因為目前網頁的文章是寫死的,接下來要透過 ngFor 動態的把文章的資料呈現在網頁上。
處理資料部分
先整理好要給 ngFor 跑的陣列資料,可以先寫死一份假資料在 app.component.ts 內。
app.component.ts
1 | import { Component } from '@angular/core'; |
在這邊因為 tslint 要求我把所有的雙引號改成單引號,為了開發方便,我先暫時把 tslint 給關閉。
就這樣我們新增了一個 atticleData
屬性,裡面是一個陣列,陣列內又有一個個物件,而每一個物件又有相關的屬性。
接著我們透過 ngFor 這個結構型指令來把這些通通顯示在畫面上吧!
處理 Template 的 HTML 部分
回過頭來處理 HTML 部分,同樣我們使用 code snippet 幫助撰寫程式碼
1 | <!-- Article START--> |
這裡的 *ngFor
後面接的語法有點像是 JavaScript 中 ForEach() 的感覺, item
代表當前陣列的元素而 atticleData
則是要遍歷的目標陣列。
蠻像 Vue 中的 v-for
指令的,如果有學習過 Vue 肯定對這個部分不陌生。
運行開發伺服器,觀察結果吧!
至此,我們已經成功地透過 ngFor 快速的把資料轉換成網頁的內容,而且程式碼相當的簡潔。
不過還需要修正最後一個小地方,那就是文章 summary 部分的 HTML 標籤居然直接被輸出了,這該怎麼辦呢?
透過屬性繫結的方式
先把 HTML 中的 這段內嵌繫結刪除,並且套用屬性繫結
1 | <!-- Article START--> |
這一段的作法是將屬性繫結在 section 標籤的 DOM 物件內的 innerHTML 屬性上,這麼一來 HTML 的標籤就會直接寫入 innerHTML 這個屬性內。
如此一來就全部搞定啦~
值得注意的是這個作法是有風險的,因為這些 HTML 可能含有一些惡意的內容,可能會導致 Cross-Site Script 的攻擊。
不過 Angular 有幫我們做到一些很基本的防護,例如在資料的部分偷偷的加一些料:
1 | atticleData = [ |
回到網頁上會發現 Angular 自動的把 script 標籤過濾掉了,所以什麼都事情都沒發生,而且還很智慧的顯示這一段提示
意思是 Angular 幫我們從 HTML內了清除了一些有害的內容,而後面的連結則是關於 XSS 的資安文件。
替 ngFor 加上索引值
最後我們要替每一篇文章的 id 補上索引值,而 ngFor 裡面提供了一個好用的方法。
1 | <!-- Article START--> |
只有在 ngFor 指令下才可以使用,能自行宣告一個變數 idx
,並且賦值為 index
,如此一來就能取得目前的索引值。
這時的文章 id 就會按照索引值增加了。
當然這邊也可以直接使用屬性繫結把資料內的 id 綁上去即可,只是這邊要介紹索引值的用法。
小結
總算是介紹完 Angular 的三種類型的指令了,有沒有覺得這些指令都似曾相似呢?就我來說的話肯定是有的,而大部分的用法都蠻接近的,只是細節上有不同之處。
希望能早日把 Angular 給掌握起來!