Angular 內(nèi)置指令

2022-07-01 10:12 更新

內(nèi)置指令

指令是為 Angular 應(yīng)用程序中的元素添加額外行為的類。使用 Angular 的內(nèi)置指令,你可以管理表單、列表、樣式以及要讓用戶看到的任何內(nèi)容。

要查看包含本指南中代碼的可工作范例,請(qǐng)參閱現(xiàn)場(chǎng)演練 / 下載范例

Angular 指令的不同類型如下:

  1. 組件 —— 帶有模板的指令。這種指令類型是最常見的指令類型。
  2. 屬性型指令 —— 更改元素、組件或其他指令的外觀或行為的指令。
  3. 結(jié)構(gòu)型指令 —— 通過添加和刪除 DOM 元素來更改 DOM 布局的指令。

本指南涵蓋了內(nèi)置的屬性型指令和結(jié)構(gòu)型指令。

內(nèi)置屬性型指令

屬性型指令會(huì)監(jiān)聽并修改其它 HTML 元素和組件的行為、Attribute 和 Property。

許多 NgModule(例如 ?RouterModule ?和 ?FormsModule ?都定義了自己的屬性型指令。最常見的屬性型指令如下:

  • ?NgClass ?—— 添加和刪除一組 CSS 類。
  • ?NgStyle ?—— 添加和刪除一組 HTML 樣式。
  • ?NgModel ?—— 將數(shù)據(jù)雙向綁定添加到 HTML 表單元素。

內(nèi)置指令只會(huì)使用公開 API。它們不會(huì)訪問任何無法被其它指令訪問的私有 API。

用 NgClass 添加和刪除類

用 ?ngClass ?同時(shí)添加或刪除多個(gè) CSS 類。

要添加或刪除單個(gè)類,請(qǐng)使用類綁定而不是 ?NgClass?。

將 NgClass 與表達(dá)式一起使用

在要設(shè)置樣式的元素上,添加 ?[ngClass]? 并將其設(shè)置為等于某個(gè)表達(dá)式。在這里,是在 ?app.component.ts? 中將 ?isSpecial ?設(shè)置為布爾值 ?true?。因?yàn)?nbsp;?isSpecial ?為 ?true?,所以 ?ngClass ?就會(huì)把 ?special ?類應(yīng)用于此 ?<div>? 上。

<!-- toggle the "special" class on/off with a property -->
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>

將 NgClass 與方法一起使用

  1. 要將 ?NgClass ?與方法一起使用,請(qǐng)將方法添加到組件類中。在下面的示例中,?setCurrentClasses()? 使用一個(gè)對(duì)象來設(shè)置屬性 ?currentClasses?,該對(duì)象根據(jù)另外三個(gè)組件屬性為 ?true ?或 ?false ?來添加或刪除三個(gè) CSS 類。
  2. 該對(duì)象的每個(gè)鍵(key)都是一個(gè) CSS 類名。如果鍵為 ?true?,則 ?ngClass ?添加該類。如果鍵為 ?false?,則 ?ngClass ?刪除該類。

    currentClasses: Record<string, boolean> = {};
    /* . . . */
    setCurrentClasses() {
      // CSS classes: added/removed per current state of component properties
      this.currentClasses =  {
        saveable: this.canSave,
        modified: !this.isUnchanged,
        special:  this.isSpecial
      };
    }
  3. 在模板中,把 ?ngClass ?屬性綁定到 ?currentClasses?,根據(jù)它來設(shè)置此元素的 CSS 類:
  4. <div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>

在這個(gè)例子中,Angular 會(huì)在初始化以及發(fā)生更改的情況下應(yīng)用這些類。完整的示例會(huì)在 ngOnInit() 中進(jìn)行初始化以及通過單擊按鈕更改相關(guān)屬性時(shí)調(diào)用 setCurrentClasses()。這些步驟對(duì)于實(shí)現(xiàn) ngClass 不是必需的。有關(guān)更多信息,請(qǐng)參見 現(xiàn)場(chǎng)演練 / 下載范例 中的 app.component.ts 和 app.component.html。

用 NgStyle 設(shè)置內(nèi)聯(lián)樣式

可以用 ?NgStyle ?根據(jù)組件的狀態(tài)同時(shí)設(shè)置多個(gè)內(nèi)聯(lián)樣式。

  1. 要使用 ?NgStyle?,請(qǐng)向組件類添加一個(gè)方法。
  2. 在下面的例子中,?setCurrentStyles()? 方法基于該組件另外三個(gè)屬性的狀態(tài),用一個(gè)定義了三個(gè)樣式的對(duì)象設(shè)置了 ?currentStyles ?屬性。

    currentStyles: Record<string, string> = {};
    /* . . . */
    setCurrentStyles() {
      // CSS styles: set per current state of component properties
      this.currentStyles = {
        'font-style':  this.canSave      ? 'italic' : 'normal',
        'font-weight': !this.isUnchanged ? 'bold'   : 'normal',
        'font-size':   this.isSpecial    ? '24px'   : '12px'
      };
    }
  3. 要設(shè)置元素的樣式,請(qǐng)將 ?ngStyle ?屬性綁定到 ?currentStyles?。
  4. <div [ngStyle]="currentStyles">
      This div is initially italic, normal weight, and extra large (24px).
    </div>

在這個(gè)例子中,Angular 會(huì)在初始化以及發(fā)生更改的情況下應(yīng)用這些類。完整的示例會(huì)在 ngOnInit() 中進(jìn)行初始化以及通過單擊按鈕更改相關(guān)屬性時(shí)調(diào)用 setCurrentClasses()。這些步驟對(duì)于實(shí)現(xiàn) ngClass 不是必需的。有關(guān)更多信息,請(qǐng)參見 現(xiàn)場(chǎng)演練 / 下載范例 中的 app.component.ts 和 app.component.html。

用 ngModel 顯示和更新屬性

可以用 ?NgModel ?指令顯示數(shù)據(jù)屬性,并在用戶進(jìn)行更改時(shí)更新該屬性。

  1. 導(dǎo)入 ?FormsModule?,并將其添加到 NgModule 的 ?imports ?列表中。
  2. import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
    /* . . . */
    @NgModule({
      /* . . . */
    
      imports: [
        BrowserModule,
        FormsModule // <--- import into the NgModule
      ],
      /* . . . */
    })
    export class AppModule { }
  3. 在 HTML 的 ?<form>? 元素上添加 ?[(ngModel)]? 綁定,并將其設(shè)置為等于此屬性,這里是 ?name?。
  4. <label for="example-ngModel">[(ngModel)]:</label>
    <input [(ngModel)]="currentItem.name" id="example-ngModel">

此 ?[(ngModel)]? 語法只能設(shè)置數(shù)據(jù)綁定屬性。

要自定義配置,你可以編寫可展開的表單,該表單將屬性綁定和事件綁定分開。使用屬性綁定來設(shè)置屬性,并使用事件綁定來響應(yīng)更改。以下示例將 ?<input>? 值更改為大寫:

<input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">

這里是所有這些變體的動(dòng)畫,包括這個(gè)大寫轉(zhuǎn)換的版本:


NgModel 和值訪問器

?NgModel ?指令適用于?ControlValueAccessor?支持的元素。Angular 為所有基本 HTML 表單元素提供了值訪問器。

要將 ?[(ngModel)]? 應(yīng)用于非表單型內(nèi)置元素或第三方自定義組件,必須編寫一個(gè)值訪問器。

編寫 Angular 組件時(shí),如果根據(jù) Angular 的雙向綁定語法命名 value 和 event 屬性,則不需要用值訪問器(ControlValueAccessor)或 ?NgModel?。

內(nèi)置結(jié)構(gòu)型指令

結(jié)構(gòu)型指令的職責(zé)是 HTML 布局。 它們塑造或重塑 DOM 的結(jié)構(gòu),這通常是通過添加、移除和操縱它們所附加到的宿主元素來實(shí)現(xiàn)的。

本節(jié)會(huì)介紹最常見的內(nèi)置結(jié)構(gòu)型指令:

  • ?NgIf ?—— 從模板中創(chuàng)建或銷毀子視圖。
  • ?NgFor ?—— 為列表中的每個(gè)條目重復(fù)渲染一個(gè)節(jié)點(diǎn)。
  • ?NgSwitch ?—— 一組在備用視圖之間切換的指令。

用 NgIf 添加或刪除元素

可以將 ?NgIf ?指令應(yīng)用于宿主元素來添加或刪除元素。

如果 ?NgIf ?為 ?false?,則 Angular 將從 DOM 中移除一個(gè)元素及其后代。然后,Angular 會(huì)銷毀其組件,從而釋放內(nèi)存和資源。

要添加或刪除元素,請(qǐng)?jiān)谝韵率纠?nbsp;?*ngIf? 綁定到條件表達(dá)式,例如 ?isActive?

<app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>

當(dāng) ?isActive ?表達(dá)式返回真值時(shí),?NgIf ?會(huì)把 ?ItemDetailComponent ?添加到 DOM 中。當(dāng)表達(dá)式為假值時(shí),?NgIf ?會(huì)從 DOM 中刪除 ?ItemDetailComponent ?并銷毀該組件及其所有子組件。

防止 null

默認(rèn)情況下,?NgIf ?會(huì)阻止顯示已綁定到空值的元素。

要使用 ?NgIf ?保護(hù) ?<div>?,請(qǐng)將 ?*ngIf="yourProperty"? 添加到此 ?<div>?。在下面的例子中,?currentCustomer ?名字出現(xiàn)了,是因?yàn)榇_實(shí)存在一個(gè) ?currentCustomer?。

<div *ngIf="currentCustomer">Hello, {{currentCustomer.name}}</div>

但是,如果該屬性為 ?null?,則 Angular 就不會(huì)顯示 ?<div>?。在這個(gè)例子中,Angular 就不會(huì)顯示 ?nullCustomer?,因?yàn)樗鼮?nbsp;?null?。

<div *ngIf="nullCustomer">Hello, <span>{{nullCustomer}}</span></div>

NgFor 條目列表

可以用 ?NgFor ?來指令顯示條目列表。

  1. 定義一個(gè) HTML 塊,該塊會(huì)決定 Angular 如何渲染單個(gè)條目。
  2. 要列出你的條目,請(qǐng)把一個(gè)簡寫形式 ?let item of items? 賦值給 ?*ngFor?。
<div *ngFor="let item of items">{{item.name}}</div>

字符串 ?"let item of items"? 會(huì)指示 Angular 執(zhí)行以下操作:

  • 將 ?items ?中的每個(gè)條目存儲(chǔ)在局部循環(huán)變量 ?item ?中
  • 讓每個(gè)條目都可用于每次迭代時(shí)的模板 HTML 中
  • 將 ?"let item of items"? 轉(zhuǎn)換為環(huán)繞宿主元素的 ?<ng-template> ?
  • 對(duì)列表中的每個(gè) ?item ?復(fù)寫這個(gè) ?<ng-template>?

復(fù)寫組件視圖

要復(fù)寫某個(gè)組件元素,請(qǐng)將 ?*ngFor? 應(yīng)用于其選擇器。在以下示例中,選擇器為 ?<app-item-detail>?。

<app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>

你可以在以下位置引用模板輸入變量,例如 ?item?:

  • 在 ?ngFor ?的宿主元素中
  • 在宿主元素的后代中,用以訪問條目的屬性

以下示例首先在插值中引用 ?item?,然后將它通過綁定傳遞給 ?<app-item-detail>? 組件的 ?item ?屬性。

<div *ngFor="let item of items">{{item.name}}</div>
<!-- . . . -->
<app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>

獲取 *ngFor 的 index

可以獲取 ?*ngFor? 的 ?index?,并在模板中使用它。

在 ?*ngFor? 中,添加一個(gè)分號(hào)和 ?let i=index? 簡寫形式。下面的例子中把 ?index ?取到一個(gè)名為 ?i? 的變量中,并將其與條目名稱一起顯示。

<div *ngFor="let item of items; let i=index">{{i + 1}} - {{item.name}}</div>

?NgFor ?指令上下文的 ?index ?屬性在每次迭代中都會(huì)返回該條目的從零開始的索引號(hào)。

Angular 會(huì)將此指令轉(zhuǎn)換為 ?<ng-template>?,然后反復(fù)使用此模板為列表中的每個(gè) ?item ?創(chuàng)建一組新的元素和綁定。

當(dāng)條件為真時(shí)復(fù)寫元素

要在特定條件為 true 時(shí)復(fù)寫 HTML 塊,請(qǐng)將 ?*ngIf? 放在 ?*ngFor? 元素的容器元素上。它們之一或兩者都可以是 ?<ng-container>?,這樣你就不必引入額外的 HTML 層次了。

由于結(jié)構(gòu)型指令會(huì)在 DOM 中添加和刪除節(jié)點(diǎn),因此每個(gè)元素只能應(yīng)用一個(gè)結(jié)構(gòu)型指令。

用 *ngFor 的 trackBy 跟蹤條目

通過跟蹤對(duì)條目列表的更改,可以減少應(yīng)用程序?qū)Ψ?wù)器的調(diào)用次數(shù)。使用 ?*ngFor? 的 ?trackBy ?屬性,Angular 只能更改和重新渲染已更改的條目,而不必重新加載整個(gè)條目列表。

  1. 向該組件添加一個(gè)方法,該方法返回 ?NgFor ?應(yīng)該跟蹤的值。這個(gè)例子中,該值是英雄的 ?id?。如果瀏覽器已經(jīng)渲染過此 ?id?,Angular 就會(huì)跟蹤它,而不會(huì)重新向服務(wù)器查詢相同的 ?id?。
  2. trackByItems(index: number, item: Item): number { return item.id; }
  3. 在簡寫表達(dá)式中,將 ?trackBy ?設(shè)置為 ?trackByItems()? 方法。
  4. <div *ngFor="let item of items; trackBy: trackByItems">
      ({{item.id}}) {{item.name}}
    </div>

更改這些 ID 會(huì)使用新的 ?item.id? 創(chuàng)建新的條目。在下面的 ?trackBy ?效果演示中,Reset items 會(huì)創(chuàng)建一些具有和以前相同的 ?item.id? 的新條目。

  • 如果沒有 ?trackBy?,這些按鈕都會(huì)觸發(fā)完全的 DOM 元素替換。
  • 有了 ?trackBy?,則只有修改了 ?id ?的按鈕才會(huì)觸發(fā)元素替換。


為沒有 DOM 元素的指令安排宿主

Angular 的 ?<ng-container>? 是一個(gè)分組元素,它不會(huì)干擾樣式或布局,因?yàn)?nbsp;Angular 不會(huì)將其放置在 DOM 中。

當(dāng)沒有單個(gè)元素承載指令時(shí),可以使用 ?<ng-container>?。

這是使用 ?<ng-container>? 的條件化段落。

<p>
  I turned the corner
  <ng-container *ngIf="hero">
    and saw {{hero.name}}. I waved
  </ng-container>
  and continued on my way.
</p>


  1. 從 ?FormsModule ?中導(dǎo)入 ?ngModel ?指令。
  2. 將 ?FormsModule ?添加到相關(guān) Angular 模塊的 imports 部分。
  3. 要有條件地排除 ?<option>?,請(qǐng)將 ?<option>? 包裹在 ?<ng-container>? 中。
  4. <div>
      Pick your favorite hero
      (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
    </div>
    <select [(ngModel)]="hero">
      <ng-container *ngFor="let h of heroes">
        <ng-container *ngIf="showSad || h.emotion !== 'sad'">
          <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
        </ng-container>
      </ng-container>
    </select>


用 NgSwitch

就像 JavaScript 的 ?switch ?語句一樣。?NgSwitch ?會(huì)根據(jù)切換條件顯示幾個(gè)可能的元素中的一個(gè)。Angular 只會(huì)將選定的元素放入 DOM。

?NgSwitch ?是一組指令(共三個(gè)):

  • ?NgSwitch ?—— 一個(gè)屬性型指令,它更改其伴生指令的行為。
  • ?NgSwitchCase ?—— 結(jié)構(gòu)型指令,當(dāng)其綁定值等于開關(guān)值時(shí)將其元素添加到 DOM 中,而在其不等于開關(guān)值時(shí)將其綁定值移除。
  • ?NgSwitchDefault ?—— 結(jié)構(gòu)型指令,當(dāng)沒有選中的 ?NgSwitchCase ?時(shí),將其宿主元素添加到 DOM 中。
  1. 在每個(gè)元素(比如?<div>?)上,把 ?[ngSwitch]? 綁定到一個(gè)返回開關(guān)值的表達(dá)式(例如 ?feature?)。盡管這個(gè)例子中 ?feature ?值是字符串,但此開關(guān)值可以是任何類型。
  2. 將各個(gè)分支元素綁定到 ?*ngSwitchCase? 和 ?*ngSwitchDefault?。
  3. <div [ngSwitch]="currentItem.feature">
      <app-stout-item    *ngSwitchCase="'stout'"    [item]="currentItem"></app-stout-item>
      <app-device-item   *ngSwitchCase="'slim'"     [item]="currentItem"></app-device-item>
      <app-lost-item     *ngSwitchCase="'vintage'"  [item]="currentItem"></app-lost-item>
      <app-best-item     *ngSwitchCase="'bright'"   [item]="currentItem"></app-best-item>
    <!-- . . . -->
      <app-unknown-item  *ngSwitchDefault           [item]="currentItem"></app-unknown-item>
    </div>
  4. 在父組件中,定義 ?currentItem ?以便可以在 ?[ngSwitch]? 表達(dá)式中使用它。
  5. currentItem!: Item;
  6. 在每個(gè)子組件中,添加一個(gè)輸入屬性 ?item?,該屬性會(huì)綁定到父組件的 ?currentItem?。以下兩個(gè)片段顯示了父組件和其中一個(gè)子組件。其他子組件與 ?StoutItemComponent ?中的相同。
  7. export class StoutItemComponent {
      @Input() item!: Item;
    }


Switch 指令也同樣適用于內(nèi)置 HTML 元素和 Web Component。 比如,你可以像下面的例子中一樣把 ?<app-best-item>? 分支替換為 ?<div>?。

<div *ngSwitchCase="'bright'"> Are you as bright as {{currentItem.name}}?</div>


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)