# Angular - 結構型指令:使用 ngFor 及 ngIf `ngFor`與`ngIf` 是屬於結構型指令的一種。 >結構型指令是透過新增和刪除 DOM 元素來更改 DOM 佈局的指令,使用簡寫語法時,Angular 在**一個元素上只允許有一個結構型指令**。 而 `ngFor` 以及`ngIf`很特別的是,他是可以直接撰寫在 html 當中的,這實在是很酷的一件事! ## ngFor `ngFor`,類似於原生 JS 的 for 循環,主要用於遍歷某個集合,並且將其值渲染到 Template 上。 首先在閱讀 Angular 文檔時,你會看見寫法是 `*ngFor`,前方有加了一個星號,而這個星號其實是一個語法糖,在 Angular 看見這個符號後,會自動將其轉換為 `<ng-template>`。 ### `<ng-template>` `<ng-template>` 是一個模板元素,通常在使用結構型指令時才會產生作用。 >Angular's `<ng-template>` element defines a template that is not rendered by default. - Angular 官方。 ### ngFor 的基本遍歷寫法 我們會使用 `<ul *ngFor = "let item of items"></ul>`,由這段寫法,我們可以遍歷 items 集合,來獲取每一個 item 的值。 ### 取得更多的值 Angular 提供了一系列的值,讓我們可以在使用 `ngFor` 遍歷集合時取得。 以下值可透過 Angular 官方文檔查詢到。 1. index: number:可迭代物件中當前條目的索引。 2. count: number:可迭代物件的長度。 3. first: boolean:如果當前條目是可迭代物件中的第一個條目則為 true。 4. last: boolean:如果當前條目是可迭代物件中的最後一個條目則為 true。 5. even: boolean:如果當前條目在可迭代物件中的索引號為偶數則為 true。 6. odd: boolean:如果當前條目在可迭代物件中的索引號為奇數則為 true。 要注意的是:**first, last, even, odd 這些值都會得到布林值**,而不是內容。 ## 使用資料綁定結合 ngFor,來創建一個 todoList ### 創建 HTML Template ```HTML= <div> <h1>TODO LIST</h1> <div class="task__input"> <!-- 這一行我們有使用到 範本參考變數、雙向綁定 --> <input #tTaskInput type="text" [(ngModel)]="taskTitle" /> <!-- 使用事件綁定來加入 task --> <button (click)="handleAddTask()">add task</button> <!-- 獲取範本參考變數的值 --> <p>{{ tTaskInput.value }}</p> </div> <!-- 使用 *ngFor 來遍歷 tasks 陣列,並取得 task 項目內容以及編號還有是否為第一個的布林值 --> <div *ngFor="let task of tasks; let i = index; let first = first"> <div class="task__item"> <span>{{ i + 1 }}</span> <input type="checkbox" /> <!-- 使用內嵌綁定來取得 task 物件的 taskTitle --> <span>{{ task.taskTitle }}</span> <!-- 判斷是否為第一個 --> <span>{{ first }}</span> <!-- 使用事件綁定來移除任務 --> <button (click)="handleRemoveTask(i)">remove</button> </div> </div> </div> ``` ### 在組件中撰寫邏輯 ```javascript= import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { taskTitle: string = ''; /* 因為 Angular 是基於 TypeScript 所撰寫的, 所以在這裡先將 tasks 陣列中每一個 task 的物件型別給訂定, 後續也可以使用 interface 來定義物件型別*/ tasks: Array<{ taskTitle: string; isCheck: boolean; }> = []; // 當按下新增按鈕時,會增加一個任務(tasks陣列會 push 一個任務物件進入) handleAddTask() { // 如果輸入值為空,則 return if (!this.taskTitle.trim()) return; this.tasks.push({ taskTitle: this.taskTitle, isCheck: false }); this.taskTitle = ''; } // 當按下刪除按鈕時,先詢問是否刪除,是,則刪除任務 handleRemoveTask(index: any) { const isRemove = window.confirm('remove task?'); if (!isRemove) return; this.tasks.splice(index, 1); } } ``` ![angular-todolist](https://hackmd.io/_uploads/By7w7GA4h.gif) ## ngIf `ngIf`,類似於原生 JS 的 If 判斷式,使用布林值來做判斷,根據 true,False,來判斷是否渲染到 Template 上。 注意:當 `ngIf` 返回 false 值時,該元素將從 DOM 中刪除,並且在刪除 HTML 元素時,作用域會被破壞,當元素重新放回檢視時,會建立一個新的作用域。(這部分可能會與 ngShow 來做比較) ### ngIf 與邏輯運算子 在 Angular 中,使用`ngIf`也可以加入 `&&`, `||`,`!` 這類的邏輯運算子來進行判斷。 ```html= <div *ngFor="let task of tasks; let i = index; let first = first"> <div class="task__item"> <span>{{ i + 1 }}</span> <input type="checkbox" (change)="handleChecked(i)" /> <span>{{ task.taskTitle }}</span> <!-- 使用 ngIf 來判斷是否任務已經打勾,若已經打勾則隱藏刪除按鈕 --> <button *ngIf="!task.isCheck" (click)="handleRemoveTask(i)"> remove </button> </div> </div> ``` ![](https://hackmd.io/_uploads/HkCzodyHn.gif) ### ngIf 與 else 原生 JS 當中,具有 `if...else` 語句,當然,`ngIf` 也有,使用方式如下: ```html= <div *ngFor="let task of tasks; let i = index; let first = first"> <div class="task__item"> <span>{{ i + 1 }}</span> <input type="checkbox" (change)="handleChecked(i)" /> <span>{{ task.taskTitle }}</span> <!-- 如果任務已經完成,則刪除 remove 按鈕,並且顯示 #tDone 的標籤元素 --> <button *ngIf="!task.isCheck; else tDone" (click)="handleRemoveTask(i)"> remove </button> <!-- ng-template 用於結構型指令,一般時候其內容並不會被顯示 --> <ng-template #tDone>DONE</ng-template> </div> </div> ``` 上方程式碼使用了 `*ngIf="!task.isCheck; else tDone"` 來判斷任務是否已經完成,若完成,則刪除 button,改為顯示 ` <ng-template #tDone>DONE</ng-template>` ![](https://hackmd.io/_uploads/rk8XCuySh.gif) ## 參考資料 - [Angular - 範本參考變數(Template reference variables)](https://dotblogs.com.tw/H20/2018/05/08/143826) - [Angular官方-ngFor](https://angular.tw/api/common/NgFor) - [新新新手閱讀 Angular 文件 - ngFor(1) - Day19](https://ithelp.ithome.com.tw/articles/10267675) - [How can I use "*ngIf else"?](https://stackoverflow.com/questions/43006550/how-can-i-use-ngif-else) ###### tags: `Angular`