Angular 8 - 指令



Angular 8 指令是與應用程式互動的 DOM 元素。通常,指令是一個TypeScript函式。當此函式執行時,Angular編譯器會在 DOM 元素內檢查它。Angular 指令以ng-開頭,其中ng代表 Angular,並使用@directive裝飾器擴充套件 HTML 標籤。

指令允許將邏輯包含在 Angular 模板中。Angular 指令可以分為三類,如下所示:

屬性指令

用於為現有 HTML 元素新增新屬性,以更改其外觀和行為。

<HTMLTag [attrDirective]='value' />

例如:

<p [showToolTip]='Tips' />

這裡,showToolTip 指的是一個示例指令,當它用於 HTML 元素時,會在使用者懸停 HTML 元素時顯示提示。

結構指令

用於在當前 HTML 文件中新增或刪除 DOM 元素。

<HTMLTag [structuralDirective]='value' />

例如:

<div *ngIf="isNeeded"> 
   Only render if the *isNeeded* value has true value. 
</div>

這裡,ngIf 是一個內建指令,用於在當前 HTML 文件中新增或刪除 HTML 元素。Angular 提供了許多內建指令,我們將在後面的章節中學習。

基於元件的指令

元件可以用作指令。每個元件都有InputOutput選項,用於在元件及其父 HTML 元素之間傳遞資料。

<component-selector-name [input-reference]="input-value"> ... </component-selector-name>

例如:

<list-item [items]="fruits"> ... </list-item>

這裡,list-item 是一個元件,items 是輸入選項。我們將在後面的章節中學習如何建立元件和高階用法。

在進入本主題之前,讓我們在 Angular 8 中建立一個示例應用程式(directive-app)來鞏固所學知識。

開啟命令提示符,使用以下命令建立一個新的 Angular 應用程式:

cd /go/to/workspace 
ng new directive-app 
cd directive-app

使用 Angular CLI 建立一個test元件,如下所示:

ng generate component test

以上命令建立一個新的元件,輸出如下:

CREATE src/app/test/test.component.scss (0 bytes) CREATE src/app/test/test.component.html (19 bytes) CREATE src/app/test/test.component.spec.ts (614 bytes) 
CREATE src/app/test/test.component.ts (262 bytes) UPDATE src/app/app.module.ts (545 bytes)

使用以下命令執行應用程式:

ng serve

DOM 概述

讓我們簡要了解一下 DOM 模型。DOM 用於定義訪問文件的標準。通常,HTML DOM 模型被構建為物件的樹。它是一個訪問 html 元素的標準物件模型。

我們可以在 Angular 8 中出於以下原因使用 DOM 模型:

  • 我們可以輕鬆地使用 DOM 元素導航文件結構。
  • 我們可以輕鬆地新增 html 元素。
  • 我們可以輕鬆地更新元素及其內容。

結構指令

結構指令透過新增或刪除元素來更改DOM的結構。它用 * 符號表示,並具有三個預定義指令NgIf、NgForNgSwitch。讓我們逐一簡要了解一下。

NgIf 指令

NgIf 指令用於根據條件為真或假來顯示或隱藏應用程式中的資料。我們可以將其新增到模板中的任何標籤。

讓我們在我們的directive-app應用程式中嘗試ngIf指令。

test.component.html中新增以下標籤。

<p>test works!</p> 
<div *ngIf="true">Display data</div>

在你的app.component.html檔案中新增測試元件,如下所示:

<app-test></app-test>

使用以下命令啟動你的伺服器(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

NgServe

如果將條件設定為ngIf=“false”,則內容將被隱藏。

ngIfElse 指令

ngIfElse類似於ngIf,不同之處在於它還提供了在失敗情況下渲染內容的選項。

讓我們透過一個示例瞭解ngIfElse的工作原理。

test.component.ts檔案中新增以下程式碼。

export class TestComponent implements OnInit { 
   isLogIn : boolean = false;
   isLogOut : boolean = true; 
}

test.component.html檔案中新增以下程式碼:

<p>ngIfElse example!</p> 
<div *ngIf="isLogIn; else isLogOut"> 
   Hello you are logged in 
</div>
<ng-template #isLogOut> 
   You're logged out.. 
</ng-template>

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

NgApplication

這裡:

isLogOut

值為true,因此它進入else塊並渲染ng-template。我們將在本章後面學習ng-template

ngFor 指令

ngFor 用於重複專案列表中的部分元素。

讓我們透過一個示例瞭解 ngFor 的工作原理。

在 test.component.ts 檔案中新增如下所示的列表:

list = [1,2,3,4,5];

test.component.html中新增ngFor指令,如下所示:

<h2>ngFor directive</h2> 
<ul> 
   <li *ngFor="let l of list">
      {{l}} 
   </li>
</ul>

這裡,let 關鍵字建立一個區域性變數,它可以在模板中的任何位置引用。let l 建立一個模板區域性變數來獲取列表元素。

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

Ngdirective

trackBy

有時,對於大型列表,ngFor的效能較低。例如,當在列表中新增新專案或刪除任何專案時,可能會觸發多個 DOM 操作。要迭代大型物件集合,我們使用trackBy

它用於跟蹤何時新增或刪除元素。它由 trackBy 方法執行。它有兩個引數:index 和 element。Index 用於唯一標識每個元素。簡單的示例定義如下。

讓我們透過一個示例瞭解 trackBy 與ngFor一起工作的方式。

test.component.ts檔案中新增以下程式碼。

export class TestComponent { 
   studentArr: any[] = [ { 
      "id": 1, 
      "name": "student1" 
   }, 
   { 
      "id": 2,
      "name": "student2" 
   }, 
   { 
      "id": 3, "name": "student3"
   },
   { 
      "id": 4, 
      "name": "student4" 
   } 
   ]; 
   trackByData(index:number, studentArr:any): number { 
      return studentArr.id; 
   }

這裡:

我們建立了:

trackByData()

方法以基於 id 的唯一方式訪問每個學生元素。

test.component.html檔案中新增以下程式碼,以便在 ngFor 中定義 trackBy 方法。

<ul> 
   <li *ngFor="let std of studentArr; trackBy: trackByData">
      {{std.name}} 
   </li>
</ul>

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

Directive

這裡,應用程式將列印學生姓名。現在,應用程式使用學生 ID 而不是物件引用來跟蹤學生物件。因此,DOM 元素不會受到影響。

NgSwitch 指令

NgSWitch用於檢查多個條件,並使 DOM 結構保持簡單易懂。

讓我們在我們的directive-app應用程式中嘗試ngSwitch指令。

test.component.ts檔案中新增以下程式碼。

export class TestComponent implements OnInit {  
   logInName = 'admin'; 
}

在 test.component.html 檔案中新增以下程式碼:

<h2>ngSwitch directive</h2> 
<ul [ngSwitch]="logInName"> 
   <li *ngSwitchCase="'user'"> 
      <p>User is logged in..</p> 
   </li> 
   <li *ngSwitchCase="'admin'"> 
      <p>admin is logged in</p> 
   </li> 
   <li *ngSwitchDefault> 
      <p>Please choose login name</p> 
   </li> 
</ul>

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

NgSwitch

這裡,我們定義了logInNameadmin。因此,它匹配第二個 SwitchCase 並列印上面與 admin 相關的訊息。

屬性指令

屬性指令執行 DOM 元素或元件的外觀或行為。一些示例包括 NgStyle、NgClass 和 NgModel。而 NgModel 是在上一章中解釋的雙向屬性資料繫結。

ngStyle

ngStyle指令用於新增動態樣式。以下示例用於將藍色應用於段落。

讓我們在我們的directive-app應用程式中嘗試ngStyle指令。

test.component.html檔案中新增以下內容。

<p [ngStyle]="{'color': 'blue', 'font-size': '14px'}"> 
   paragraph style is applied using ngStyle 
</p>

使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

ngStyle

ngClass

ngClass用於在 HTML 元素中新增或刪除 CSS 類。

讓我們在我們的directive-app應用程式中嘗試ngClass指令。

使用以下命令建立一個類User

ng g class User

你將看到以下響應:

CREATE src/app/user.spec.ts (146 bytes) 
CREATE src/app/user.ts (22 bytes)

轉到src/app/user.ts檔案並新增以下程式碼:

export class User { 
   userId : number; userName : string; 
}

這裡,我們在User類中建立了兩個屬性userIduserName

開啟test.component.ts檔案並新增以下更改:

import { User } from '../user'; 
export class TestComponent implements OnInit {  
   users: User[] = [ 
      { 
         "userId": 1, 
         "userName": 'User1' 
      }, 
      { 
         "userId": 2, 
         "userName": 'User2' 
      }, 
   ]; 
}

這裡,我們聲明瞭一個區域性變數 users 並用 2 個使用者物件初始化它。

開啟test.component.css檔案並新增以下程式碼

.highlight { 
   color: red; 
}

開啟你的test.component.html檔案並新增以下程式碼:

<div class="container"> 
   <br/> 
   <div *ngFor="let user of users" [ngClass]="{ 
      'highlight':user.userName === 'User1' 
   }"> 
      {{ user.userName }} 
   </div> 
</div>

這裡:

我們應用了ngClassUser1,因此它將突出顯示User1

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

ngClass

自定義指令

Angular 提供了使用使用者定義的指令擴充套件 angular 指令的選項,這稱為自定義指令。讓我們在本節中學習如何建立自定義指令。

讓我們嘗試在我們的directive-app應用程式中建立自定義指令。

Angular CLI 提供以下命令來建立自定義指令。

ng generate directive customstyle

執行此命令後,你將看到以下響應:

CREATE src/app/customstyle.directive.spec.ts (244 bytes) 
CREATE src/app/customstyle.directive.ts (151 bytes) UPDATE src/app/app.module.ts (1115 bytes)

開啟app.module.ts。指令將透過declarations元資料在AppModule中配置。

import { CustomstyleDirective } from './customstyle.directive'; 
@NgModule({ 
   declarations: [ 
      AppComponent, 
      TestComponent, 
      CustomstyleDirective 
   ] 
})

開啟customstyle.directive.ts檔案並新增以下程式碼:

import { Directive, ElementRef } from '@angular/core'; 
@Directive({ 
   selector: '[appCustomstyle]' 
}) 
export class CustomstyleDirective {
   constructor(el: ElementRef) { 
      el.nativeElement.style.fontSize = '24px'; 
   } 
}

這裡,constructor方法使用CustomStyleDirective獲取元素作為el。然後,它訪問 el 的樣式並使用 CSS 屬性將其字型大小設定為24px

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

Custom directives

ng-template

ng-template用於建立動態且可重用的模板。它是一個虛擬元素。如果你使用ng-template編譯程式碼,則它會在 DOM 中轉換為註釋。

例如:

讓我們在test.component.html頁面中新增以下程式碼。

<h3>ng-template</h3> 
<ng-template>ng-template tag is a virtual element</ng-template>

如果你執行應用程式,它將只打印h3元素。檢查你的頁面原始碼,模板顯示在註釋部分,因為它是一個虛擬元素,所以它不會呈現任何內容。我們需要將ng-template與 Angular 指令一起使用。

通常,指令會發出與其關聯的 HTML 標籤。有時,我們不需要標籤,只需要內容。例如,在下面的示例中,將發出 li。

<li *ngFor="let item in list">{{ item }}</li>

我們可以使用ng-template安全地跳過li標籤。

ng-template 與結構指令

ng-template應始終用於ngIf、ngForngSwitch指令內部以呈現結果。

讓我們假設一個簡單的程式碼。

<ng-template [ngIf]=true> 
   <div><h2>ng-template works!</h2></div> 
</ng-template>

這裡,如果ngIf條件為真,它將列印 div 元素內的資訊。類似地,你也可以使用ngForngSwitch指令。

NgForOf 指令

ngForOf 也是一個結構指令,用於渲染集合中的專案。下面的示例用於演示ng-template 內部的 ngForOf 指令。

import { Component, OnInit } from '@angular/core'; 
@Component({ 
   selector: 'app-test', 
   template: ` 
   <div> 
   <ng-template ngFor let-item [ngForOf]="Fruits" let-i="index"> 
   <p>{{i}}</p> 
   </ng-template> 
   </div>` 
   , 
   styleUrls: ['./test.component.css'] 
}) 
export class TestComponent implements OnInit { 
   Fruits = ["mango","apple","orange","grapes"]; 
   ngOnInit() 
   { 
   } 
}

如果執行應用程式,它將顯示每個元素的索引,如下所示:

0 
1 
2 
3

元件指令

元件指令基於元件。實際上,每個元件都可以用作指令。元件提供 @Input 和 @Output 裝飾器,用於在父元件和子元件之間傳送和接收資訊。

讓我們嘗試在我們的 directive-app 應用程式中使用元件作為指令。

使用以下命令建立一個新的 ChildComponent

ng generate component child
CREATE src/app/child/child.component.html (20 bytes) CREATE src/app/child/child.component.spec.ts (621 bytes) 
CREATE src/app/child/child.component.ts (265 bytes) CREATE src/app/child/child.component.css (0 bytes) UPDATE src/app/app.module.ts (466 bytes)

開啟 child.component.ts 並新增以下程式碼:

@Input() userName: string;

在這裡,我們為 ChildComponent 設定一個輸入屬性。

開啟 child.component.html 並新增以下程式碼:

<p>child works!</p> 
<p>Hi {{ userName }}</p>

在這裡,我們使用 userName 值來歡迎使用者。

開啟 test.component.ts 並新增以下程式碼:

name: string = 'Peter';

開啟 test.component.html 並新增以下程式碼:

<h1>Test component</h1> 
<app-child [userName]="name"><app-child>

在這裡,我們使用 AppComponent 作為帶有輸入屬性的指令在 TestComponent 內。

最後,使用以下命令啟動你的應用程式(如果尚未啟動):

ng serve

現在,執行你的應用程式,你將看到以下響應:

[](images/directive-app/component_as_directive.PNG)

工作示例

讓我們在我們的 ExpenseManager 應用程式中新增一個新元件來列出支出條目。

開啟命令提示符並轉到專案根資料夾。

cd /go/to/expense-manager

啟動應用程式。

ng serve

使用以下命令建立一個新的元件 ExpenseEntryListComponent

ng generate component ExpenseEntryList

輸出

輸出如下:

CREATE src/app/expense-entry-list/expense-entry-list.component.html (33 bytes) 
CREATE src/app/expense-entry-list/expense-entry-list.component.spec.ts (700 bytes) 
CREATE src/app/expense-entry-list/expense-entry-list.component.ts (315 bytes) 
CREATE src/app/expense-entry-list/expense-entry-list.component.css (0 bytes) 
UPDATE src/app/app.module.ts (548 bytes)

在這裡,該命令建立了 ExpenseEntryList 元件並在 AppModule 中更新了必要的程式碼。

ExpenseEntry 匯入到 ExpenseEntryListComponent 元件中 (src/app/expense-entry-list/expense-entry-list.component)

import { ExpenseEntry } from '../expense-entry';

新增一個方法 getExpenseEntries() 以在 ExpenseEntryListComponent (src/app/expense-entry-list/expense-entry-list.component) 中返回支出條目的列表(模擬專案)。

getExpenseEntries() : ExpenseEntry[] { 
   let mockExpenseEntries : ExpenseEntry[] = [ 
      { id: 1, 
         item: "Pizza", 
         amount: Math.floor((Math.random() * 10) + 1), 
         category: "Food", 
         location: "Mcdonald", 
         spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), 
         createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, 
      { id: 1, 
         item: "Pizza", 
         amount: Math.floor((Math.random() * 10) + 1), 
         category: "Food", 
         location: "KFC", 
         spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), 
         createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, 
      { id: 1,
         item: "Pizza",
         amount: Math.floor((Math.random() * 10) + 1), 
         category: "Food", 
         location: "Mcdonald", 
         spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), 
         createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, 
      { id: 1, 
         item: "Pizza", 
         amount: Math.floor((Math.random() * 10) + 1), 
         category: "Food", 
         location: "KFC", 
         spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), 
         createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, 
      { id: 1, 
         item: "Pizza", 
         amount: Math.floor((Math.random() * 10) + 1), 
         category: "Food", 
         location: "KFC", 
         spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), 
         createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) 
      }, 
   ]; 
   return mockExpenseEntries; 
}

宣告一個區域性變數 expenseEntries 並載入如下所示的模擬支出條目列表:

title: string; 
expenseEntries: ExpenseEntry[]; 
constructor() { } 
ngOnInit() { 
   this.title = "Expense Entry List"; 
   this.expenseEntries = this.getExpenseEntries(); 
}

開啟模板檔案 (src/app/expense-entry-list/expense-entry-list.component.html) 並以表格形式顯示模擬條目。

<!-- Page Content -->
<div class="container"> 
   <div class="row"> 
      <div class="col-lg-12 text-center" style="padding-top: 20px;">
         <div class="container" style="padding-left: 0px; padding-right: 0px;"> 
            <div class="row"> 
               <div class="col-sm" style="text-align: left;"> 
                  {{ title }} 
               </div> 
               <div class="col-sm" style="text-align: right;"> 
                  <button type="button" class="btn btn-primary">Edit</button> 
               </div> 
            </div> 
         </div> 
         <div class="container box" style="margin-top: 10px;"> 
            <table class="table table-striped"> 
               <thead> 
                  <tr> 
                     <th>Item</th> 
                     <th>Amount</th> 
                     <th>Category</th> 
                     <th>Location</th> 
                     <th>Spent On</th> 
                  </tr> 
               </thead> 
               <tbody> 
                  <tr *ngFor="let entry of expenseEntries"> 
                     <th scope="row">{{ entry.item }}</th> 
                     <th>{{ entry.amount }}</th> 
                     <td>{{ entry.category }}</td> 
                     <td>{{ entry.location }}</td> 
                     <td>{{ entry.spendOn | date: 'short' }}</td> 
                  </tr> 
               </tbody> 
            </table> 
         </div> 
      </div> 
   </div> 
</div>

這裡:

  • 使用了 Bootstrap 表格。tabletable-striped 將根據 Bootstrap 樣式標準設定表格樣式。

  • 使用 ngFor 遍歷 expenseEntries 並生成表格行。

開啟 AppComponent 模板 src/app/app.component.html 幷包含 ExpenseEntryListComponent 並刪除 ExpenseEntryComponent,如下所示:

... 
<app-expense-entry-list></app-expense-entry-list>

最後,應用程式的輸出如下所示。

AppComponent
廣告