99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Angular9 模板語(yǔ)法

2020-06-30 18:00 更新

這是一篇關(guān)于 Angular 模板語(yǔ)言的技術(shù)大全。 在本篇中解釋了模板語(yǔ)言的基本原理,并描述了你將在本教程的學(xué)習(xí)中遇到的大部分語(yǔ)法。

Angular 應(yīng)用管理著用戶(hù)之所見(jiàn)和所為,并通過(guò) Component 類(lèi)的實(shí)例(組件)和面向用戶(hù)的模板交互來(lái)實(shí)現(xiàn)這一點(diǎn)。

從使用模型-視圖-控制器 (MVC) 或模型-視圖-視圖模型 (MVVM) 的經(jīng)驗(yàn)中,很多開(kāi)發(fā)人員都熟悉了組件和模板這兩個(gè)概念。 在 Angular 中,組件扮演著控制器或視圖模型的角色,模板則扮演視圖的角色。

這是一篇關(guān)于 Angular 模板語(yǔ)言的技術(shù)大全。 它解釋了模板語(yǔ)言的基本原理,并描述了你將在文檔中其它地方遇到的大部分語(yǔ)法。

模板中的 HTML

HTML 是 Angular 模板的語(yǔ)言。幾乎所有的 HTML 語(yǔ)法都是有效的模板語(yǔ)法。 但值得注意的例外是 <script>元素,它被禁用了,以阻止腳本注入攻擊的風(fēng)險(xiǎn)。(實(shí)際上,<script> 只是被忽略了。)

有些合法的 HTML 被用在模板中是沒(méi)有意義的。比如<html>、<body><base> 這幾個(gè)元素在這其中并沒(méi)有扮演有用的角色。剩下的所有元素基本上就都有其所用了。

可以通過(guò)組件和指令來(lái)擴(kuò)展模板中的 HTML 詞匯。它們看上去就是新元素和屬性。接下來(lái)將學(xué)習(xí)如何通過(guò)數(shù)據(jù)綁定來(lái)動(dòng)態(tài)獲取/設(shè)置 DOM(文檔對(duì)象模型)的值。

首先看看數(shù)據(jù)綁定的第一種形式 —— 插值,它展示了模板的 HTML 可以有多豐富。

插值與模板表達(dá)式

插值能讓你把計(jì)算后的字符串合并到 HTML 元素標(biāo)簽之間和屬性賦值語(yǔ)句內(nèi)的文本中。模板表達(dá)式則是用來(lái)供你求出這些字符串的。

插值 {{...}}

所謂 "插值" 是指將表達(dá)式嵌入到標(biāo)記文本中。 默認(rèn)情況下,插值會(huì)用雙花括號(hào) {{ }} 作為分隔符。

在下面的代碼片段中,{{ currentCustomer }} 就是插值的例子。

Path:"src/app/app.component.html"

<h3>Current customer: {{ currentCustomer }}</h3>

花括號(hào)之間的文本currentCustomer通常是組件屬性的名字。Angular 會(huì)把這個(gè)名字替換為響應(yīng)組件屬性的字符串值。

Path:"src/app/app.component.html"

<p>{{title}}</p>
<div><img src="{{itemImageUrl}}"></div>

在上面的示例中,Angular 計(jì)算 titleitemImageUrl 屬性并填充空白,首先顯示一些標(biāo)題文本,然后顯示圖像。

一般來(lái)說(shuō),括號(hào)間的素材是一個(gè)模板表達(dá)式,Angular 先對(duì)它求值,再把它轉(zhuǎn)換成字符串。 下列插值通過(guò)把括號(hào)中的兩個(gè)數(shù)字相加說(shuō)明了這一點(diǎn):

Path:"src/app/app.component.html"

<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}.</p>

這個(gè)表達(dá)式可以調(diào)用宿主組件的方法,就像下面用的 getVal()

Path:"src/app/app.component.html"

<!-- "The sum of 1 + 1 is not 4" -->
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}.</p>

Angular 對(duì)所有雙花括號(hào)中的表達(dá)式求值,把求值的結(jié)果轉(zhuǎn)換成字符串,并把它們跟相鄰的字符串字面量連接起來(lái)。最后,把這個(gè)組合出來(lái)的插值結(jié)果賦給元素或指令的屬性。

你看上去似乎正在將結(jié)果插入元素標(biāo)簽之間,并將其賦值給屬性。 但實(shí)際上,插值是一種特殊語(yǔ)法,Angular 會(huì)將其轉(zhuǎn)換為屬性綁定。

注:
- 如果你想用別的分隔符來(lái)代替 {{ }},也可以通過(guò) Component 元數(shù)據(jù)中的 interpolation 選項(xiàng)來(lái)配置插值分隔符。

模板表達(dá)式

模板表達(dá)式會(huì)產(chǎn)生一個(gè)值,并出現(xiàn)在雙花括號(hào) {{ }} 中。 Angular 執(zhí)行這個(gè)表達(dá)式,并把它賦值給綁定目標(biāo)的屬性,這個(gè)綁定目標(biāo)可能是 HTML 元素、組件或指令。

{{1 + 1}} 中所包含的模板表達(dá)式是 1 + 1。 在屬性綁定中會(huì)再次看到模板表達(dá)式,它出現(xiàn)在 = 右側(cè)的引號(hào)中,就像這樣:[property]="expression"

在語(yǔ)法上,模板表達(dá)式與 JavaScript 很像。很多 JavaScript 表達(dá)式都是合法的模板表達(dá)式,但也有一些例外。

你不能使用那些具有或可能引發(fā)副作用的 JavaScript 表達(dá)式,包括:

  • 賦值 (=, +=, -=, ...)。

  • new、typeof、instanceof 等運(yùn)算符。

  • 使用 ;, 串聯(lián)起來(lái)的表達(dá)式。

  • 自增和自減運(yùn)算符:++--。

  • 一些 ES2015+ 版本的運(yùn)算符。

和 JavaScript 語(yǔ)法的其它顯著差異包括:

  • 不支持位運(yùn)算,比如 |&。

  • 新的模板表達(dá)式運(yùn)算符,例如 |,?. 和 !

表達(dá)式上下文

典型的表達(dá)式上下文就是這個(gè)組件實(shí)例,它是各種綁定值的來(lái)源。 在下面的代碼片段中,雙花括號(hào)中的 recommended 和引號(hào)中的 itemImageUrl2 所引用的都是 AppComponent 中的屬性。

Path:"src/app/app.component.html"

<h4>{{recommended}}</h4>
<img [src]="itemImageUrl2">

表達(dá)式也可以引用模板中的上下文屬性,例如模板輸入變量,

let customer,或模板引用變量 #customerInput。

Path:"src/app/app.component.html (template input variable)"

<ul>
  <li *ngFor="let customer of customers">{{customer.name}}</li>
</ul>

Path:"src/app/app.component.html (template reference variable)"

<label>Type something:
  <input #customerInput>{{customerInput.value}}
</label>

表達(dá)式中的上下文變量是由模板變量、指令的上下文變量(如果有)和組件的成員疊加而成的。 如果你要引用的變量名存在于一個(gè)以上的命名空間中,那么,模板變量是最優(yōu)先的,其次是指令的上下文變量,最后是組件的成員。

上一個(gè)例子中就體現(xiàn)了這種命名沖突。組件具有一個(gè)名叫 customer 的屬性,而 *ngFor 聲明了一個(gè)也叫 customer 的模板變量。

注:
- 在 {{customer.name}} 表達(dá)式中的 customer 實(shí)際引用的是模板變量,而不是組件的屬性。

  • 模板表達(dá)式不能引用全局命名空間中的任何東西,比如 windowdocument。它們也不能調(diào)用 console.logMath.max。 它們只能引用表達(dá)式上下文中的成員。

表達(dá)式使用指南

當(dāng)使用模板表達(dá)式時(shí),請(qǐng)遵循下列要素:

  1. 非常簡(jiǎn)單

雖然也可以寫(xiě)復(fù)雜的模板表達(dá)式,不過(guò)最好避免那樣做。

屬性名或方法調(diào)用應(yīng)該是常態(tài),但偶然使用邏輯取反 ! 也是可以的。 其它情況下,應(yīng)該把應(yīng)用程序和業(yè)務(wù)邏輯限制在組件中,這樣它才能更容易開(kāi)發(fā)和測(cè)試。

  1. 執(zhí)行迅速

Angular 會(huì)在每個(gè)變更檢測(cè)周期后執(zhí)行模板表達(dá)式。 變更檢測(cè)周期會(huì)被多種異步活動(dòng)觸發(fā),比如 Promise 解析、HTTP 結(jié)果、定時(shí)器時(shí)間、按鍵或鼠標(biāo)移動(dòng)。

表達(dá)式應(yīng)該快速結(jié)束,否則用戶(hù)就會(huì)感到拖沓,特別是在較慢的設(shè)備上。 當(dāng)計(jì)算代價(jià)較高時(shí),應(yīng)該考慮緩存那些從其它值計(jì)算得出的值。

  1. 沒(méi)有可見(jiàn)的副作用

模板表達(dá)式除了目標(biāo)屬性的值以外,不應(yīng)該改變應(yīng)用的任何狀態(tài)。

這條規(guī)則是 Angular “單向數(shù)據(jù)流”策略的基礎(chǔ)。 永遠(yuǎn)不用擔(dān)心讀取組件值可能改變另外的顯示值。 在一次單獨(dú)的渲染過(guò)程中,視圖應(yīng)該總是穩(wěn)定的。

冪等的表達(dá)式是最理想的,因?yàn)樗鼪](méi)有副作用,并且可以提高 Angular 的變更檢測(cè)性能。 用 Angular 術(shù)語(yǔ)來(lái)說(shuō),冪等表達(dá)式總會(huì)返回完全相同的東西,除非其依賴(lài)值之一發(fā)生了變化。

在單獨(dú)的一次事件循環(huán)中,被依賴(lài)的值不應(yīng)該改變。 如果冪等的表達(dá)式返回一個(gè)字符串或數(shù)字,連續(xù)調(diào)用它兩次,也應(yīng)該返回相同的字符串或數(shù)字。 如果冪等的表達(dá)式返回一個(gè)對(duì)象(包括 Date 或 Array),連續(xù)調(diào)用它兩次,也應(yīng)該返回同一個(gè)對(duì)象的引用。

注:
- 對(duì)于 *ngFor,這種行為有一個(gè)例外。*ngFor 具有 trackBy 功能,在迭代對(duì)象時(shí)它可以處理對(duì)象的相等性。詳情參見(jiàn) 帶 trackBy*ngFor。

模板語(yǔ)句

模板語(yǔ)句用來(lái)響應(yīng)由綁定目標(biāo)(如 HTML 元素、組件或指令)觸發(fā)的事件。 模板語(yǔ)句將在事件綁定一節(jié)看到,它出現(xiàn)在 = 號(hào)右側(cè)的引號(hào)中,就像這樣:(event)="statement"。

Path:"src/app/app.component.html"

<button (click)="deleteHero()">Delete hero</button>

模板語(yǔ)句有副作用。 這是事件處理的關(guān)鍵。因?yàn)槟阋鶕?jù)用戶(hù)的輸入更新應(yīng)用狀態(tài)。

響應(yīng)事件是 Angular 中“單向數(shù)據(jù)流”的另一面。 在一次事件循環(huán)中,可以隨意改變?nèi)魏蔚胤降娜魏螙|西。

和模板表達(dá)式一樣,模板語(yǔ)句使用的語(yǔ)言也像 JavaScript。 模板語(yǔ)句解析器和模板表達(dá)式解析器有所不同,特別之處在于它支持基本賦值 (=) 和表達(dá)式鏈 (;)。

然而,某些 JavaScript 語(yǔ)法和模板表達(dá)式語(yǔ)法仍然是不允許的:

  • new 運(yùn)算符

自增和自減運(yùn)算符:++--

操作并賦值,例如 +=-=

位運(yùn)算符,例如 |&

管道運(yùn)算符

語(yǔ)句上下文

和表達(dá)式中一樣,語(yǔ)句只能引用語(yǔ)句上下文中 —— 通常是正在綁定事件的那個(gè)組件實(shí)例。

典型的語(yǔ)句上下文就是當(dāng)前組件的實(shí)例。 (click)="deleteHero()" 中的 deleteHero 就是這個(gè)數(shù)據(jù)綁定組件上的一個(gè)方法。

Path:"src/app/app.component.html"

<button (click)="deleteHero()">Delete hero</button>

語(yǔ)句上下文可以引用模板自身上下文中的屬性。 在下面的例子中,就把模板的 $event 對(duì)象、模板輸入變量 (let hero)和模板引用變量 (#heroForm)傳給了組件中的一個(gè)事件處理器方法。

Path:"src/app/app.component.html"

<button (click)="onSave($event)">Save</button>
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>

模板上下文中的變量名的優(yōu)先級(jí)高于組件上下文中的變量名。在上面的 deleteHero(hero) 中,hero 是一個(gè)模板輸入變量,而不是組件中的 hero 屬性。

語(yǔ)句指南

模板語(yǔ)句不能引用全局命名空間的任何東西。比如不能引用 windowdocument,也不能調(diào)用 console.logMath.max。

和表達(dá)式一樣,避免寫(xiě)復(fù)雜的模板語(yǔ)句。 常規(guī)是函數(shù)調(diào)用或者屬性賦值。

綁定語(yǔ)法:概覽

數(shù)據(jù)綁定是一種機(jī)制,用來(lái)協(xié)調(diào)用戶(hù)可見(jiàn)的內(nèi)容,特別是應(yīng)用數(shù)據(jù)的值。 雖然也可以手動(dòng)從 HTML 中推送或拉取這些值,但是如果將這些任務(wù)轉(zhuǎn)交給綁定框架,應(yīng)用就會(huì)更易于編寫(xiě)、閱讀和維護(hù)。 你只需聲明數(shù)據(jù)源和目標(biāo) HTML 元素之間的綁定關(guān)系就可以了,框架會(huì)完成其余的工作。

Angular 提供了多種數(shù)據(jù)綁定方式。綁定類(lèi)型可以分為三類(lèi),按數(shù)據(jù)流的方向分為:

  1. 單向:從數(shù)據(jù)源到視圖

  • 綁定類(lèi)型:插值、屬性、Attribute、CSS類(lèi)、樣式。

  • 語(yǔ)法:

    {{expression}}
    [target]="expression"
    bind-target="expression"

  1. 單向:從視圖到數(shù)據(jù)源

  • 綁定類(lèi)型:事件

  • 語(yǔ)法:

    (target)="statement"
    on-target="statement"

  • 雙向:視圖到數(shù)據(jù)源到視圖

  • 綁定類(lèi)型:雙向

  • 語(yǔ)法:

    [(target)]="expression"
    bindon-target="expression"

除插值以外的其它綁定類(lèi)型在等號(hào)的左側(cè)都有一個(gè)“目標(biāo)名稱(chēng)”,由綁定符 []() 包起來(lái), 或者帶有前綴:bind-,on-bindon-。

綁定的“目標(biāo)”是綁定符內(nèi)部的屬性或事件:[]()[()]。

在綁定時(shí)可以使用來(lái)源指令的每個(gè)公共成員。 你無(wú)需進(jìn)行任何特殊操作即可在模板表達(dá)式或語(yǔ)句內(nèi)訪(fǎng)問(wèn)指令的成員。

數(shù)據(jù)綁定與 HTML

在正常的 HTML 開(kāi)發(fā)過(guò)程中,你使用 HTML 元素來(lái)創(chuàng)建視覺(jué)結(jié)構(gòu), 通過(guò)把字符串常量設(shè)置到元素的 attribute 來(lái)修改那些元素。

<div class="special">Plain old HTML</div>


<img src="images/item.png">
<button disabled>Save</button>

使用數(shù)據(jù)綁定,你可以控制按鈕狀態(tài)等各個(gè)方面:

Path:"src/app/app.component.html"

<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>

注:
- 這里綁定到的是按鈕的 DOM 元素的 disabled 這個(gè) Property,而不是 Attribute

  • 這是數(shù)據(jù)綁定的通用規(guī)則。數(shù)據(jù)綁定使用 DOM 元素、組件和指令的 Property,而不是 HTML 的 Attribute。

HTML attribute 與 DOM property 的對(duì)比

理解 HTML 屬性和 DOM 屬性之間的區(qū)別,是了解 Angular 綁定如何工作的關(guān)鍵。Attribute 是由 HTML 定義的。Property 是從 DOM(文檔對(duì)象模型)節(jié)點(diǎn)訪(fǎng)問(wèn)的。

  • 一些 HTML Attribute 可以 1:1 映射到 Property;例如,“ id”。

  • 某些 HTML Attribute 沒(méi)有相應(yīng)的 Property。例如,aria-*。

  • 某些 DOM Property 沒(méi)有相應(yīng)的 Attribute。例如,textContent。

重要的是要記住,HTML Attribute 和 DOM Property 是不同的,就算它們具有相同的名稱(chēng)也是如此。 在 Angular 中,HTML Attribute 的唯一作用是初始化元素和指令的狀態(tài)。

模板綁定使用的是 Property 和事件,而不是 Attribute。

編寫(xiě)數(shù)據(jù)綁定時(shí),你只是在和目標(biāo)對(duì)象的 DOM Property 和事件打交道。

注:
- 該通用規(guī)則可以幫助你建立 HTML Attribute 和 DOM Property 的思維模型: 屬性負(fù)責(zé)初始化 DOM 屬性,然后完工。Property 值可以改變;Attribute 值則不能。

  • 此規(guī)則有一個(gè)例外。 可以通過(guò) setAttribute() 來(lái)更改 Attribute,接著它會(huì)重新初始化相應(yīng)的 DOM 屬性。

示例 1:<input>

當(dāng)瀏覽器渲染 <input type="text" value="Sarah"> 時(shí),它會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的 DOM 節(jié)點(diǎn),其 value Property 已初始化為 “Sarah”。

<input type="text" value="Sarah">

當(dāng)用戶(hù)在 <input> 中輸入 Sally 時(shí),DOM 元素的 value Property 將變?yōu)?Sally。 但是,如果使用 input.getAttribute('value') 查看 HTML 的 Attribute value,則可以看到該 attribute 保持不變 —— 它返回了 Sarah。

HTML 的 value 這個(gè) attribute 指定了初始值;DOM 的 value 這個(gè) property 是當(dāng)前值。

示例 2:禁用按鈕

disabled Attribute 是另一個(gè)例子。按鈕的 disabled Property 默認(rèn)為 false,因此按鈕是啟用的。

當(dāng)你添加 disabled Attribute 時(shí),僅僅它的出現(xiàn)就將按鈕的 disabled Property 初始化成了 true,因此該按鈕就被禁用了。

<button disabled>Test Button</button>

添加和刪除 disabled , Attribute 會(huì)禁用和啟用該按鈕。 但是,Attribute 的值無(wú)關(guān)緊要,這就是為什么你不能通過(guò)編寫(xiě) <button disabled="false">仍被禁用</button> 來(lái)啟用此按鈕的原因。

要控制按鈕的狀態(tài),請(qǐng)?jiān)O(shè)置 disabled Property。

雖然技術(shù)上說(shuō)你可以設(shè)置 [attr.disabled] 屬性綁定,但是它們的值是不同的,Property 綁定要求一個(gè)布爾值,而其相應(yīng)的 Attribute 綁定則取決于該值是否為 null。例子如下:

&
lt;input [disabled]="condition ? true : false"&
lt;input [attr.disabled]="condition ? 'disabled' : null"&

通常,要使用 Property 綁定而不是 Attribute 綁定,因?yàn)樗庇^(是一個(gè)布爾值),語(yǔ)法更短,并且性能更高。

綁定類(lèi)型與綁定目標(biāo)

數(shù)據(jù)綁定的目標(biāo)是 DOM 中的對(duì)象。 根據(jù)綁定類(lèi)型,該目標(biāo)可以是 Property 名(元素、組件或指令的)、事件名(元素、組件或指令的),有時(shí)是 Attribute 名。下表中總結(jié)了不同綁定類(lèi)型的目標(biāo)。

  1. 綁定類(lèi)型:屬性。

  • 目標(biāo):元素的 property 、組件的 property 、指令的 property 。

  • 示例:

    <img [src]="heroImageUrl">
    <app-hero-detail [hero]="currentHero"></app-hero-detail>
    <div [ngClass]="{'special': isSpecial}"></div>

  1. 綁定類(lèi)型:事件。

  • 目標(biāo):元素的事件、組件的事件、指令的事件。

  • 示例:

    <button (click)="onSave()">Save</button>
    <app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail>
    <div (myClick)="clicked=$event" clickable>click me</div>

  1. 綁定類(lèi)型:雙向。

  • 目標(biāo):事件與 property。

  • 示例:

    <input [(ngModel)]="name">

  1. 綁定類(lèi)型:Attribute 。

  • 目標(biāo):attribute(例外情況)。

  • 示例:

    <button [attr.aria-label]="help">help</button>

  1. 綁定類(lèi)型:CSS 類(lèi)。

  • 目標(biāo):class property 。

  • 示例:

    <div [class.special]="isSpecial">Special</div>

  1. 綁定類(lèi)型:樣式。

  • 目標(biāo):style property 。

  • 示例:

    <button [style.color]="isSpecial ? 'red' : 'green'">

Property 綁定 [property]

使用 Property 綁定到目標(biāo)元素或指令 @Input() 裝飾器的 set 型屬性。

單向輸入

Property 綁定的值在一個(gè)方向上流動(dòng),從組件的 Property 變?yōu)槟繕?biāo)元素的 Property。

你不能使用屬性綁定從目標(biāo)元素讀取或拉取值。同樣的,你也不能使用屬性綁定在目標(biāo)元素上調(diào)用方法。如果元素要引發(fā)事件,則可以使用事件綁定來(lái)監(jiān)聽(tīng)它們。

示例:

最常見(jiàn)的 Property 綁定將元素的 Property 設(shè)置為組件的 Property 值。例子之一是將 img 元素的 src Property 綁定到組件的 itemImageUrl Property

Path:"src/app/app.component.html"

<img [src]="itemImageUrl">

這是綁定到 colSpan Property 的示例。請(qǐng)注意,它不是 colspan,后者是 Attribute,用小寫(xiě)的 s 拼寫(xiě)。

Path:"src/app/app.component.html"

<!-- Notice the colSpan property is camel case -->
<tr><td [colSpan]="2">Span 2 columns</td></tr>

另一個(gè)例子是當(dāng)組件說(shuō)它 isUnchanged(未改變)時(shí)禁用按鈕:

Path:"src/app/app.component.html"

<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Disabled Button</button>

另一個(gè)例子是設(shè)置指令的屬性:

Path:"src/app/app.component.html"

<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p

另一種方法是設(shè)置自定義組件的模型屬性 —— 這是一種父級(jí)和子級(jí)組件進(jìn)行通信的好辦法:

Path:"src/app/app.component.html"

<app-item-detail [childItem]="parentItem"></app-item-detail>

綁定目標(biāo)

包裹在方括號(hào)中的元素屬性名標(biāo)記著目標(biāo)屬性。下列代碼中的目標(biāo)屬性是 image 元素的 src 屬性。

Path:"src/app/app.component.html"

<img [src]="itemImageUrl">

還有一種使用 bind- 前綴的替代方案:

Path:"src/app/app.component.html"

<img bind-src="itemImageUrl">

在大多數(shù)情況下,目標(biāo)名都是 Property 名,雖然它看起來(lái)像 Attribute 名。因此,在這個(gè)例子中,src<img> 元素屬性的名稱(chēng)。

元素屬性可能是最常見(jiàn)的綁定目標(biāo),但 Angular 會(huì)先去看這個(gè)名字是否是某個(gè)已知指令的屬性名,就像下面的例子中一樣:

Path:"src/app/app.component.html"

<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p>

從技術(shù)上講,Angular 將這個(gè)名稱(chēng)與指令的 @Input() 進(jìn)行匹配,它來(lái)自指令的 inputs 數(shù)組中列出的 Property 名稱(chēng)之一或是用 @Input() 裝飾的屬性。這些輸入都映射到指令自身的屬性。

如果名字沒(méi)有匹配上已知指令或元素的屬性,Angular 就會(huì)報(bào)告“未知指令”的錯(cuò)誤。

注:
- 盡管目標(biāo)名稱(chēng)通常是 Property 的名稱(chēng),但是在 Angular 中,有幾個(gè)常見(jiàn)屬性會(huì)自動(dòng)將 Attribute 映射為 Property。這些包括 class / className,nnerHtml / innerHTMLtabindex / abIndex。

消除副作用

模板表達(dá)的計(jì)算應(yīng)該沒(méi)有明顯的副作用。表達(dá)式語(yǔ)言本身或你編寫(xiě)模板表達(dá)式的方式在一定程度上有所幫助。你不能為屬性綁定表達(dá)式中的任何內(nèi)容賦值,也不能使用遞增和遞減運(yùn)算符。

例如,假設(shè)你有一個(gè)表達(dá)式,該表達(dá)式調(diào)用了具有副作用的屬性或方法。該表達(dá)式可以調(diào)用類(lèi)似 getFoo() 的函數(shù),只有你知道 getFoo() 做了什么。如果 getFoo() 更改了某些內(nèi)容,而你恰巧綁定到該內(nèi)容,則 Angular 可能會(huì)也可能不會(huì)顯示更改后的值。Angular 可能會(huì)檢測(cè)到更改并拋出警告錯(cuò)誤。最佳實(shí)踐是堅(jiān)持使用屬性和返回值并避免副作用的方法。

返回正確的類(lèi)型

模板表達(dá)式的計(jì)算結(jié)果應(yīng)該是目標(biāo)屬性所需要的值類(lèi)型。如果 target 屬性需要一個(gè)字符串,則返回一個(gè)字符串;如果需要一個(gè)數(shù)字,則返回一個(gè)數(shù)字;如果需要一個(gè)對(duì)象,則返回一個(gè)對(duì)象,依此類(lèi)推。

在下面的例子中,temDetailComponentchildItem 屬性需要一個(gè)字符串,而這正是你要發(fā)送給屬性綁定的內(nèi)容:

Path:"src/app/app.component.html"

<app-item-detail [childItem]="parentItem"></app-item-detail>

你可以查看 ItemDetailComponent 來(lái)確認(rèn)這一點(diǎn),它的 @Input 類(lèi)型設(shè)為了字符串:

Path:"src/app/item-detail/item-detail.component.ts (setting the @Input() type)"

@Input() childItem: string;

如你所見(jiàn),AppComponent 中的 parentItem 是一個(gè)字符串,而 ItemDetailComponent 需要的就是字符串:

Path:"src/app/app.component.ts"

parentItem = 'lamp';

傳入對(duì)象

前面的簡(jiǎn)單示例演示了傳入字符串的情況。要傳遞對(duì)象,其語(yǔ)法和思想是相同的。

在這種情況下,ItemListComponent 嵌套在 AppComponent 中,并且 items 屬性需要一個(gè)對(duì)象數(shù)組。

Path:"src/app/app.component.html"

<app-item-list [items]="currentItems"></app-item-list>

items 屬性是在 ItemListComponent 中用 Item 類(lèi)型聲明的,并帶有 @Input() 裝飾器:

Path:"src/app/item-list.component.ts"

@Input() items: Item[];

在此示例應(yīng)用程序中,Item是具有兩個(gè)屬性的對(duì)象。一個(gè) id 和一個(gè) name。

Path:"src/app/item.ts"

export interface Item {
  id: number;
  name: string;
}

當(dāng)另一個(gè)文件 "mock-items.ts" 中存在一個(gè)條目列表時(shí),你可以在 "app.component.ts" 中指定另一個(gè)條目,以便渲染新條目:

Path:"src/app.component.ts"

currentItems = [{
  id: 21,
  name: 'phone'
}];

在這個(gè)例子中,你只需要確保你所提供的對(duì)象數(shù)組的類(lèi)型,也就是這個(gè) Item 的類(lèi)型是嵌套組件 `ItemListComponent 所需要的類(lèi)型。

在此示例中,AppComponen 指定了另一個(gè) item 對(duì)象( urrentItems )并將其傳給嵌套的 ItemListComponent。ItemListComponent 之所以能夠使用 currentItems 是因?yàn)樗c "item.ts" 中定義的 Item 對(duì)象的類(lèi)型相匹配。在 "item.ts" 文件中,ItemListComponent 獲得了其對(duì) item 的定義。

方括號(hào)

方括號(hào) [] 告訴 Angular 計(jì)算該模板表達(dá)式。如果省略括號(hào),Angular 會(huì)將字符串視為常量,并使用該字符串初始化目標(biāo)屬性 :

Path:"src/app.component.html"

<app-item-detail childItem="parentItem"></app-item-detail>

省略方括號(hào)將渲染字符串 parentItem,而不是 parentItem 的值。

一次性字符串初始化

當(dāng)滿(mǎn)足下列條件時(shí),應(yīng)該省略括號(hào):

  • 目標(biāo)屬性接受字符串值。

  • 字符串是一個(gè)固定值,你可以直接將其放入模板中。

  • 這個(gè)初始值永不改變。

你通常會(huì)以這種方式在標(biāo)準(zhǔn) HTML 中初始化屬性,并且它對(duì)指令和組件的屬性初始化同樣有效。 下面的示例將 StringInitComponent 中的 prefix 屬性初始化為固定字符串,而不是模板表達(dá)式。Angular 設(shè)置它,然后就不管它了。

Path:"src/app/app.component.html"

<app-string-init prefix="This is a one-time initialized string."></app-string-init>

另一方面,[item] 綁定仍然是與組件的 currentItems 屬性的實(shí)時(shí)綁定。

屬性綁定與插值

你通常得在插值和屬性綁定之間做出選擇。 下列這幾對(duì)綁定做的事情完全相同:

Path:"src/app/app.component.html"

<p><img src="{{itemImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="itemImageUrl"> is the <i>property bound</i> image.</p>


<p><span>"{{interpolationTitle}}" is the <i>interpolated</i> title.</span></p>
<p>"<span [innerHTML]="propertyTitle"></span>" is the <i>property bound</i> title.</p>

在許多情況下,插值是屬性綁定的便捷替代法。當(dāng)要把數(shù)據(jù)值渲染為字符串時(shí),雖然可讀性方面傾向于插值,但沒(méi)有技術(shù)上的理由偏愛(ài)一種形式。但是,將元素屬性設(shè)置為非字符串的數(shù)據(jù)值時(shí),必須使用屬性綁定。

內(nèi)容安全

假設(shè)如下惡意內(nèi)容:

Path:"src/app/app.component.ts"

evilTitle = 'Template <script>alert("evil never sleeps")</script> Syntax';

在組件模板中,內(nèi)容可以與插值一起使用:

Path:"src/app/app.component.html"

<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>

幸運(yùn)的是,Angular 數(shù)據(jù)綁定對(duì)于危險(xiǎn)的 HTML 高度戒備。在上述情況下,HTML 將按原樣顯示,而 Javascript 不執(zhí)行。Angular 不允許帶有 script 標(biāo)簽的 HTML 泄漏到瀏覽器中,無(wú)論是插值還是屬性綁定。

不過(guò),在下列示例中,Angular 會(huì)在顯示值之前先對(duì)它們進(jìn)行無(wú)害化處理。

Path:"src/app/app.component.html"

<!--
 Angular generates a warning for the following line as it sanitizes them
 WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).
-->
 <p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>

插值處理 <script> 標(biāo)記與屬性綁定的方式不同,但是這兩種方法都可以使內(nèi)容無(wú)害。以下是 evilTitle 示例的瀏覽器輸出。

"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
"Template Syntax" is the property bound evil title.

attribute、class 和 style 綁定

模板語(yǔ)法為那些不太適合使用屬性綁定的場(chǎng)景提供了專(zhuān)門(mén)的單向數(shù)據(jù)綁定形式。

要在運(yùn)行中的應(yīng)用查看 Attribute 綁定、類(lèi)綁定和樣式綁定,請(qǐng)參見(jiàn) 現(xiàn)場(chǎng)演練 / 下載范例 特別是對(duì)于本節(jié)。

attribute 綁定

可以直接使用 Attribute 綁定設(shè)置 Attribute 的值。一般來(lái)說(shuō),綁定時(shí)設(shè)置的是目標(biāo)的 Property,而 Attribute 綁定是唯一的例外,它創(chuàng)建和設(shè)置的是 Attribute。

通常,使用 Property 綁定設(shè)置元素的 Property 優(yōu)于使用字符串設(shè)置 Attribute。但是,有時(shí)沒(méi)有要綁定的元素的 Property,所以其解決方案就是 Attribute 綁定。

考慮 ARIASVG。它們都純粹是 Attribute,不對(duì)應(yīng)于元素的 Property,也不能設(shè)置元素的 Property。 在這些情況下,就沒(méi)有要綁定到的目標(biāo) Property。

Attribute 綁定的語(yǔ)法類(lèi)似于 Property 綁定,但其括號(hào)之間不是元素的 Property,而是由前綴 attr、點(diǎn)( . )和 Attribute 名稱(chēng)組成。然后,你就可以使用能解析為字符串的表達(dá)式來(lái)設(shè)置該 Attribute 的值,或者當(dāng)表達(dá)式解析為 null 時(shí)刪除該 Attribute。

attribute 綁定的主要用例之一是設(shè)置 ARIA attribute(譯注:ARIA 指無(wú)障礙功能,用于給殘障人士訪(fǎng)問(wèn)互聯(lián)網(wǎng)提供便利), 就像這個(gè)例子中一樣:

Path:"src/app/app.component.html"

<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>

colspancolSpan
注意 colspan AttributecolSpan Property 之間的區(qū)別。

如果你這樣寫(xiě):

&
lt;tr&<td colspan="{{1 + 1}}"&Three-Four</td&</tr&

你會(huì)收到如下錯(cuò)誤:

&
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property

如錯(cuò)誤消息所示,<td& 元素沒(méi)有 colspan 這個(gè) Property。這是正確的,因?yàn)?colspan 是一個(gè) Attribute,而 colSpan (colSpan 中的 S 是大寫(xiě))則是相應(yīng)的 Property。插值和 Property 綁定只能設(shè)置 Property,不能設(shè)置 Attribute。

相反,你可以使用 Property 綁定并將其改寫(xiě)為:

&Path:"src/app/app.component.html"

lt;!-- Notice the colSpan property is camel case --&
lt;tr&<td [colSpan]="1 + 1"&Three-Four</td&</tr&

類(lèi)綁定

下面是在普通 HTML 中不用綁定來(lái)設(shè)置 class Attribute 的方法:

<!-- standard class attribute setting -->


<div class="foo bar">Some text</div>

你還可以使用類(lèi)綁定來(lái)為一個(gè)元素添加和移除 CSS 類(lèi)。

要?jiǎng)?chuàng)建單個(gè)類(lèi)的綁定,請(qǐng)使用 class 前綴,緊跟一個(gè)點(diǎn)(.),再跟上 CSS 類(lèi)名,比如 [class.foo]="hasFoo"。 當(dāng)綁定表達(dá)式為真值的時(shí)候,Angular 就會(huì)加上這個(gè)類(lèi),為假值則會(huì)移除,但 undefined 是假值中的例外,參見(jiàn)樣式委派 部分。

要想創(chuàng)建多個(gè)類(lèi)的綁定,請(qǐng)使用通用的 [class] 形式來(lái)綁定類(lèi),而不要帶點(diǎn),比如 [class]="classExpr"。 該表達(dá)式可以是空格分隔的類(lèi)名字符串,或者用一個(gè)以類(lèi)名為鍵、真假值表達(dá)式為值的對(duì)象。 當(dāng)使用對(duì)象格式時(shí),Angular 只會(huì)加上那些相關(guān)的值為真的類(lèi)名。

一定要注意,在對(duì)象型表達(dá)式中(如 object、Array、Map、Set 等),當(dāng)這個(gè)類(lèi)列表改變時(shí),對(duì)象的引用也必須修改。僅僅修改其屬性而不修改對(duì)象引用是無(wú)法生效的。

如果有多處綁定到了同一個(gè)類(lèi)名,出現(xiàn)的沖突將根據(jù)樣式的優(yōu)先級(jí)規(guī)則進(jìn)行解決。

綁定類(lèi)型 語(yǔ)法 輸入類(lèi)型 輸入值示例
單個(gè)類(lèi)綁定 [class.foo]="hasFoo" boolean OR undefined OR null true, false
多個(gè)類(lèi)綁定 [class]="classExpr" string OR {[key: string]: boolean / undefined / null} OR Array<string> | "my-class-1 my-class-2 my-class-3" OR {foo: true, bar: false} OR ['foo', 'bar']`

注:
- 多個(gè)類(lèi)綁定的輸入類(lèi)型按順序?qū)?yīng)一個(gè)示例,而不是一個(gè)類(lèi)型對(duì)應(yīng)多個(gè)示例。

盡管此技術(shù)適用于切換單個(gè)類(lèi)名,但在需要同時(shí)管理多個(gè)類(lèi)名時(shí)請(qǐng)考慮使用 NgClass 指令。

樣式綁定

下面演示了如何不通過(guò)綁定在普通 HTML 中設(shè)置 style 屬性:

<!-- standard style attribute setting -->
<div style="color: blue">Some text</div>

你還可以通過(guò)樣式綁定來(lái)動(dòng)態(tài)設(shè)置樣式。

要想創(chuàng)建單個(gè)樣式的綁定,請(qǐng)以 style 前綴開(kāi)頭,緊跟一個(gè)點(diǎn)(.),再跟著 CSS 樣式的屬性名,比如 [style.width]="width"。 該屬性將會(huì)被設(shè)置為綁定表達(dá)式的值,該值通常為字符串。 不過(guò)你還可以添加一個(gè)單位表達(dá)式,比如 em%,這時(shí)候該值就要是一個(gè) number 類(lèi)型。

注:
- 樣式屬性命名方法可以用中線(xiàn)命名法,像上面的一樣 也可以用駝峰式命名法,如 fontSize。

如果要切換多個(gè)樣式,你可以直接綁定到 [style] 屬性而不用點(diǎn)(比如,[style]="styleExpr")。賦給 [style] 的綁定表達(dá)式通常是一系列樣式組成的字符串,比如 "width: 100px; height: 100px;"。

你也可以把該表達(dá)式格式化成一個(gè)以樣式名為鍵、以樣式值為值的對(duì)象,比如 {width: '100px', height: '100px'}。一定要注意,對(duì)于任何對(duì)象型的表達(dá)式( 如 object,Array,Map,Set 等),當(dāng)這個(gè)樣式列表改變時(shí),對(duì)象的引用也必須修改。僅僅修改其屬性而不修改對(duì)象引用是無(wú)法生效的。。

如果有多處綁定了同一個(gè)樣式屬性,則會(huì)使用樣式的優(yōu)先級(jí)規(guī)則來(lái)解決沖突。

綁定類(lèi)型 語(yǔ)法 輸入類(lèi)型 輸入值示例
單一樣式綁定 [style.width]="width" string OR undefined OR null "100px"
帶單位的單一樣式綁定 [style.width.px]="width" number OR undefined OR null 100
多個(gè)樣式綁定 [style]="styleExpr" string OR {[key: string]: string / undefined / null} OR Array<string>` ['width', '100px']

NgStyle 指令可以作為 [style] 綁定的替代指令。但是,應(yīng)該把上面這種 [style] 樣式綁定語(yǔ)法作為首選,因?yàn)殡S著 Angular 中樣式綁定的改進(jìn),NgStyle 將不再提供重要的價(jià)值,并最終在未來(lái)的某個(gè)版本中刪除。

樣式的優(yōu)先級(jí)規(guī)則

一個(gè) HTML 元素可以把它的 CSS 類(lèi)列表和樣式值綁定到多個(gè)來(lái)源(例如,來(lái)自多個(gè)指令的宿主 host 綁定)。

當(dāng)對(duì)同一個(gè)類(lèi)名或樣式屬性存在多個(gè)綁定時(shí),Angular 會(huì)使用一組優(yōu)先級(jí)規(guī)則來(lái)解決沖突,并確定最終哪些類(lèi)或樣式會(huì)應(yīng)用到該元素中。

樣式的優(yōu)先級(jí)規(guī)則(從高到低)

  1. 模板綁定

  1. 屬性綁定(例如 <div [class.foo]="hasFoo"& 或 <div [style.color]="color"&)

1. Map 綁定(例如,<div [class]="classExpr"& 或 <div [style]="styleExpr"& )

2. 靜態(tài)值(例如 <div class="foo"& 或 <div style="color: blue"& )

  1. 指令宿主綁定

  1. 屬性綁定(例如,host: {'[class.foo]': 'hasFoo'} 或 host: {'[style.color]': 'color'} )

1. Map 綁定(例如,host: {'[class]': 'classExpr'} 或者 host: {'[style]': 'styleExpr'} )

2. 靜態(tài)值(例如,host: {'class': 'foo'} 或 host: {'style': 'color: blue'} )

  1. 組件宿主綁定

  1. 屬性綁定(例如,host: {'[class.foo]': 'hasFoo'} 或 host: {'[style.color]': 'color'} )

1. Map 綁定(例如,host: {'[class]': 'classExpr'} 或者 host: {'[style]': 'styleExpr'} )

2. 靜態(tài)值(例如,host: {'class': 'foo'} 或 host: {'style': 'color: blue'} )

某個(gè)類(lèi)或樣式綁定越具體,它的優(yōu)先級(jí)就越高。

對(duì)具體類(lèi)(例如 [class.foo] )的綁定優(yōu)先于一般化的 [class] 綁定,對(duì)具體樣式(例如 [style.bar] )的綁定優(yōu)先于一般化的 [style] 綁定。

Path:"src/app/app.component.html"

<h3>Basic specificity</h3>


<!-- The `class.special` binding will override any value for the `special` class in `classExpr`.  -->
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>


<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`.  -->
<div [style.color]="color" [style]="styleExpr">Some text.</div>

當(dāng)處理不同來(lái)源的綁定時(shí),也適用這種基于具體度的規(guī)則。 某個(gè)元素可能在聲明它的模板中有一些綁定、在所匹配的指令中有一些宿主綁定、在所匹配的組件中有一些宿主綁定。

模板中的綁定是最具體的,因?yàn)樗鼈冎苯硬⑶椅ㄒ坏貞?yīng)用于該元素,所以它們具有最高的優(yōu)先級(jí)。

指令的宿主綁定被認(rèn)為不太具體,因?yàn)橹噶羁梢栽诙鄠€(gè)位置使用,所以它們的優(yōu)先級(jí)低于模板綁定。

指令經(jīng)常會(huì)增強(qiáng)組件的行為,所以組件的宿主綁定優(yōu)先級(jí)最低。

Path:"src/app/app.component.html"

<h3>Source specificity</h3>


<!-- The `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>


<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>

另外,綁定總是優(yōu)先于靜態(tài)屬性。

在下面的例子中,class[class] 具有相似的具體度,但 [class] 綁定優(yōu)先,因?yàn)樗莿?dòng)態(tài)的。

Path:"src/app/app.component.html"

<h3>Dynamic vs static</h3>


<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
<div class="special" [class]="classExpr">Some text.</div>


<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
<div style="color: blue" [style]="styleExpr">Some text.</div>

委托優(yōu)先級(jí)較低的樣式

更高優(yōu)先級(jí)的樣式可以使用 undefined 值“委托”給低級(jí)的優(yōu)先級(jí)樣式。雖然把 style 屬性設(shè)置為 null 可以確保該樣式被移除,但把它設(shè)置為 undefined 會(huì)導(dǎo)致 Angular 回退到該樣式的次高優(yōu)先級(jí)。

例如,考慮以下模板:

Path:"src/app/app.component.html"

<comp-with-host-binding dirWithHostBinding></comp-with-host-binding>

想象一下,dirWithHostBinding 指令和 comp-with-host-binding 組件都有 [style.width] 宿主綁定。在這種情況下,如果 dirWithHostBinding 把它的綁定設(shè)置為 undefined,則 width 屬性將回退到 comp-with-host-binding 主機(jī)綁定的值。但是,如果 dirWithHostBinding 把它的綁定設(shè)置為 null,那么 width 屬性就會(huì)被完全刪除。

事件綁定 (event)

事件綁定允許你監(jiān)聽(tīng)某些事件,比如按鍵、鼠標(biāo)移動(dòng)、點(diǎn)擊和觸屏。

Angular 的事件綁定語(yǔ)法由等號(hào)左側(cè)帶圓括號(hào)的目標(biāo)事件和右側(cè)引號(hào)中的模板語(yǔ)句組成。 下面事件綁定監(jiān)聽(tīng)按鈕的點(diǎn)擊事件。每當(dāng)點(diǎn)擊發(fā)生時(shí),都會(huì)調(diào)用組件的 onSave() 方法。

目標(biāo)事件

如前所述,其目標(biāo)就是此按鈕的單擊事件。

Path:"src/app/app.component.html"

<button (click)="onSave($event)">Save</button>

有些人更喜歡帶 on- 前綴的備選形式,稱(chēng)之為規(guī)范形式:

Path:"src/app/app.component.html"

<button on-click="onSave($event)">on-click Save</button>

元素事件可能是更常見(jiàn)的目標(biāo),但 Angular 會(huì)先看這個(gè)名字是否能匹配上已知指令的事件屬性,就像下面這個(gè)例子:

Path:"src/app/app.component.html"

<h4>myClick is an event on the custom ClickDirective:</h4>
<button (myClick)="clickMessage=$event" clickable>click with myClick</button>
{{clickMessage}}

如果這個(gè)名字沒(méi)能匹配到元素事件或已知指令的輸出屬性,Angular 就會(huì)報(bào)“未知指令”錯(cuò)誤。

$event 和事件處理語(yǔ)句

在事件綁定中,Angular 會(huì)為目標(biāo)事件設(shè)置事件處理器。

當(dāng)事件發(fā)生時(shí),這個(gè)處理器會(huì)執(zhí)行模板語(yǔ)句。 典型的模板語(yǔ)句通常涉及到響應(yīng)事件執(zhí)行動(dòng)作的接收器,例如從 HTML 控件中取得值,并存入模型。

綁定會(huì)通過(guò)名叫 $event 的事件對(duì)象傳遞關(guān)于此事件的信息(包括數(shù)據(jù)值)。

事件對(duì)象的形態(tài)取決于目標(biāo)事件。如果目標(biāo)事件是原生 DOM 元素事件, $event 就是 DOM 事件對(duì)象,它有像 targettarget.value 這樣的屬性。

考慮這個(gè)示例:

Path:"src/app/app.component.html"

<input [value]="currentItem.name"
       (input)="currentItem.name=$event.target.value" >
without NgModel

上面的代碼在把輸入框的 value 屬性綁定到 name 屬性。 要監(jiān)聽(tīng)對(duì)值的修改,代碼綁定到輸入框的 input 事件。 當(dāng)用戶(hù)造成更改時(shí),input 事件被觸發(fā),并在包含了 DOM 事件對(duì)象 ($event) 的上下文中執(zhí)行這條語(yǔ)句。

要更新 name 屬性,就要通過(guò)路徑 $event.target.value 來(lái)獲取更改后的值。

如果事件屬于指令(回想一下,組件是指令的一種),那么 $event 具體是什么由指令決定。

使用 EventEmitter 實(shí)現(xiàn)自定義事件

通常,指令使用 Angular EventEmitter 來(lái)觸發(fā)自定義事件。 指令創(chuàng)建一個(gè) EventEmitter 實(shí)例,并且把它作為屬性暴露出來(lái)。 指令調(diào)用 EventEmitter.emit(payload) 來(lái)觸發(fā)事件,可以傳入任何東西作為消息載荷。 父指令通過(guò)綁定到這個(gè)屬性來(lái)監(jiān)聽(tīng)事件,并通過(guò) $event 對(duì)象來(lái)訪(fǎng)問(wèn)載荷。

假設(shè) ItemDetailComponent 用于顯示英雄的信息,并響應(yīng)用戶(hù)的動(dòng)作。 雖然 ItemDetailComponent 包含刪除按鈕,但它自己并不知道該如何刪除這個(gè)英雄。 最好的做法是觸發(fā)事件來(lái)報(bào)告“刪除用戶(hù)”的請(qǐng)求。

下面的代碼節(jié)選自 ItemDetailComponent:

Path:"src/app/item-detail/item-detail.component.html (template)"

<img src="{{itemImageUrl}}" [style.display]="displayNone">
<span [style.text-decoration]="lineThrough">{{ item.name }}
</span>
<button (click)="delete()">Delete</button>

Path:"src/app/item-detail/item-detail.component.ts (deleteRequest)"

// This component makes a request but it can't actually delete a hero.
@Output() deleteRequest = new EventEmitter<Item>();


delete() {
  this.deleteRequest.emit(this.item);
  this.displayNone = this.displayNone ? '' : 'none';
  this.lineThrough = this.lineThrough ? '' : 'line-through';
}

組件定義了 deleteRequest 屬性,它是 EventEmitter 實(shí)例。 當(dāng)用戶(hù)點(diǎn)擊刪除時(shí),組件會(huì)調(diào)用 delete() 方法,讓 EventEmitter 發(fā)出一個(gè) Item 對(duì)象。

現(xiàn)在,假設(shè)有個(gè)宿主的父組件,它綁定了 ItemDetailComponentdeleteRequest 事件。

Path:"src/app/app.component.html (event-binding-to-component)"

<app-item-detail (deleteRequest)="deleteItem($event)" [item]="currentItem"></app-item-detail>

當(dāng) deleteRequest 事件觸發(fā)時(shí),Angular 調(diào)用父組件的 deleteItem 方法, 在 $event 變量中傳入要?jiǎng)h除的英雄(來(lái)自 ItemDetail)。

模板語(yǔ)句有副作用

雖然模板表達(dá)式不應(yīng)該有副作用,但是模板語(yǔ)句通常會(huì)有。這里的 deleteItem() 方法就有一個(gè)副作用:它刪除了一個(gè)條目。

刪除這個(gè)英雄會(huì)更新模型,還可能觸發(fā)其它修改,包括向遠(yuǎn)端服務(wù)器的查詢(xún)和保存。 這些變更通過(guò)系統(tǒng)進(jìn)行擴(kuò)散,并最終顯示到當(dāng)前以及其它視圖中。

雙向綁定 [(...)]

雙向綁定為你的應(yīng)用程序提供了一種在組件類(lèi)及其模板之間共享數(shù)據(jù)的方式。

雙向綁定的基礎(chǔ)知識(shí)

雙向綁定會(huì)做兩件事:

  1. 設(shè)置特定的元素屬性。

  1. 監(jiān)聽(tīng)元素的變更事件。

Angular 為此提供了一種特殊的雙向數(shù)據(jù)綁定語(yǔ)法 [()]。[()] 語(yǔ)法將屬性綁定的括號(hào) [] 與事件綁定的括號(hào) () 組合在一起。

[()] 語(yǔ)法很容易想明白:該元素具有名為 x 的可設(shè)置屬性和名為 xChange 的相應(yīng)事件。 SizerComponent 就是用的這種模式。它具有一個(gè)名為 size 的值屬性和一個(gè)與之相伴的 sizeChange 事件:

Path:"src/app/sizer.component.ts"

import { Component, Input, Output, EventEmitter } from '@angular/core';


@Component({
  selector: 'app-sizer',
  templateUrl: './sizer.component.html',
  styleUrls: ['./sizer.component.css']
})
export class SizerComponent {




  @Input()  size: number | string;
  @Output() sizeChange = new EventEmitter<number>();


  dec() { this.resize(-1); }
  inc() { this.resize(+1); }


  resize(delta: number) {
    this.size = Math.min(40, Math.max(8, +this.size + delta));
    this.sizeChange.emit(this.size);
  }


}

Path:"src/app/sizer.component.html"

<div>
  <button (click)="dec()" title="smaller">-</button>
  <button (click)="inc()" title="bigger">+</button>
  <label [style.font-size.px]="size">FontSize: {{size}}px</label>
</div>

size 的初始值來(lái)自屬性綁定的輸入值。單擊按鈕可在最小值/最大值范圍內(nèi)增大或減小 size,然后帶上調(diào)整后的大小發(fā)出 sizeChange 事件。

下面的例子中,AppComponent.fontSize 被雙向綁定到 SizerComponent

Path:"src/app/app.component.html (two-way-1)"

<app-sizer [(size)]="fontSizePx"></app-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>

AppComponent.fontSizePx 建立初始 SizerComponent.size 值。

Path:"src/app/app.component.ts"

fontSizePx = 16;

單擊按鈕就會(huì)通過(guò)雙向綁定更新 AppComponent.fontSizePx。修改后的 AppComponent.fontSizePx 值將傳遞到樣式綁定,從而使顯示的文本更大或更小。

雙向綁定語(yǔ)法實(shí)際上是屬性綁定和事件綁定的語(yǔ)法糖。 Angular 將 izerComponent 的綁定分解成這樣:

Path:"src/app/app.component.html (two-way-2)"

<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer>

$event 變量包含了 SizerComponent.sizeChange 事件的荷載。 當(dāng)用戶(hù)點(diǎn)擊按鈕時(shí),Angular 將 $event 賦值給 AppComponent.fontSizePx

表單中的雙向綁定

與單獨(dú)的屬性綁定和事件綁定相比,雙向綁定語(yǔ)法非常方便。將雙向綁定與 HTML 表單元素(例如 <input><select>)一起使用會(huì)很方便。但是,沒(méi)有哪個(gè)原生 HTML 元素會(huì)遵循 x 值和 xChange 事件的命名模式。

內(nèi)置指令

Angular 提供了兩種內(nèi)置指令:屬性型指令和結(jié)構(gòu)型指令。

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

屬性型指令會(huì)監(jiān)聽(tīng)并修改其它 HTML 元素和組件的行為、AttributeProperty。 它們通常被應(yīng)用在元素上,就好像它們是 HTML 屬性一樣,因此得名屬性型指令。

許多 NgModule(例如 RouterModuleFormsModule )都定義了自己的屬性型指令。最常見(jiàn)的屬性型指令如下:

  1. NgClass —— 添加和刪除一組 CSS 類(lèi)。

用 ngClass 同時(shí)添加或刪除幾個(gè) CSS 類(lèi)。

Path:"src/app/app.component.html"

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

注:
- 要添加或刪除單個(gè)類(lèi),請(qǐng)使用類(lèi)綁定而不是 NgClass

考慮一個(gè) setCurrentClasses() 組件方法,該方法設(shè)置一個(gè)組件屬性 currentClasses,該對(duì)象具有一個(gè)根據(jù)其它三個(gè)組件屬性的 true / false 狀態(tài)來(lái)添加或刪除三個(gè) CSS 類(lèi)的對(duì)象。該對(duì)象的每個(gè)鍵(key)都是一個(gè) CSS 類(lèi)名。如果要添加上該類(lèi),則其值為 true,反之則為 false。

Path:"src/app/app.component.html"

    currentClasses: {};
    setCurrentClasses() {
      // CSS classes: added/removed per current state of component properties
      this.currentClasses =  {
        'saveable': this.canSave,
        'modified': !this.isUnchanged,
        'special':  this.isSpecial
      };
    }

NgClass 屬性綁定到 currentClasses,根據(jù)它來(lái)設(shè)置此元素的 CSS 類(lèi):

Path:"src/app/app.component.html"

    <div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>

注:
- 請(qǐng)記住,在這種情況下,你要在初始化時(shí)和它依賴(lài)的屬性發(fā)生變化時(shí)調(diào)用 setCurrentClasses()

  1. NgStyle —— 添加和刪除一組 HTML 樣式。

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

不用 NgStyle

有些情況下,要考慮使用樣式綁定來(lái)設(shè)置單個(gè)樣式值,而不使用 NgStyle。

Path:"src/app/app.component.html"

    <div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
      This div is x-large or smaller.
    </div>

但是,如果要同時(shí)設(shè)置多個(gè)內(nèi)聯(lián)樣式,請(qǐng)使用 NgStyle 指令。

下面的例子是一個(gè) setCurrentStyles() 方法,它基于該組件另外三個(gè)屬性的狀態(tài),用一個(gè)定義了三個(gè)樣式的對(duì)象設(shè)置了 currentStyles 屬性。

Path:"src/app/app.component.ts"

    currentStyles: {};
    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'
      };
    }

ngStyle 屬性綁定到 currentStyles,來(lái)根據(jù)它設(shè)置此元素的樣式:

Path:"src/app/app.component.html"

    <div [ngStyle]="currentStyles">
      This div is initially italic, normal weight, and extra large (24px).
    </div>

注:
- 請(qǐng)記住,無(wú)論是在初始時(shí)還是其依賴(lài)的屬性發(fā)生變化時(shí),都要調(diào)用 setCurrentStyles()。

  1. NgModel —— 將數(shù)據(jù)雙向綁定添加到 HTML 表單元素。

NgModel 指令允許你顯示數(shù)據(jù)屬性并在用戶(hù)進(jìn)行更改時(shí)更新該屬性。這是一個(gè)例子:

Path:"src/app/app.component.html (NgModel example)"

    <label for="example-ngModel">[(ngModel)]:</label>
    <input [(ngModel)]="currentItem.name" id="example-ngModel">

導(dǎo)入 FormsModule 以使用 ngModel

要想在雙向數(shù)據(jù)綁定中使用 ngModel 指令,必須先導(dǎo)入 FormsModule 并將其添加到 NgModuleimports 列表中。要了解關(guān)于 FormsModulengModel 的更多信息,參見(jiàn)表單一章。

記住,要導(dǎo)入 FormsModule 才能讓 [(ngModel)] 可用,如下所示:

Path:"src/app/app.module.ts (FormsModule import)"

    import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
    /* . . . */
    @NgModule({
    /* . . . */


      imports: [
        BrowserModule,
        FormsModule // <--- import into the NgModule
      ],
    /* . . . */
    })
    export class AppModule { }

通過(guò)分別綁定到 <input> 元素的 value 屬性和 input 事件,可以達(dá)到同樣的效果:

Path:"src/app/app.component.html"

    <label for="without">without NgModel:</label>
    <input [value]="currentItem.name" (input)="currentItem.name=$event.target.value" id="without">

為了簡(jiǎn)化語(yǔ)法,ngModel 指令把技術(shù)細(xì)節(jié)隱藏在其輸入屬性 ngModel 和輸出屬性 ngModelChange 的后面:

Path:"src/app/app.component.html"

    <label for="example-change">(ngModelChange)="...name=$event":</label>
    <input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">

ngModel 輸入屬性會(huì)設(shè)置該元素的值,并通過(guò) ngModelChange 的輸出屬性來(lái)監(jiān)聽(tīng)元素值的變化。

NgModel 和值訪(fǎng)問(wèn)器

這些技術(shù)細(xì)節(jié)是針對(duì)每種具體元素的,因此 NgModel 指令僅適用于通過(guò) ControlValueAccessor 適配過(guò)這種協(xié)議的元素。Angular 已經(jīng)為所有基本的 HTML 表單元素提供了值訪(fǎng)問(wèn)器,表單一章示范了如何綁定到它們。

在編寫(xiě)適當(dāng)?shù)闹翟L(fǎng)問(wèn)器之前,不能將 [(ngModel)] 應(yīng)用于非表單的原生元素或第三方自定義組件。欲知詳情,參見(jiàn)DefaultValueAccessor上的 API 文檔。

你不一定非用為所編寫(xiě)的 Angular 組件提供值訪(fǎng)問(wèn)器,因?yàn)槟氵€可以把值屬性和事件屬性命名為符合 Angular 的基本雙向綁定語(yǔ)法的形式,并完全跳過(guò) NgModel。雙向綁定部分的 sizer 是此技術(shù)的一個(gè)示例。

單獨(dú)的 ngModel 綁定是對(duì)綁定到元素的原生屬性方式的一種改進(jìn),但你可以使用 [(ngModel)] 語(yǔ)法來(lái)通過(guò)單個(gè)聲明簡(jiǎn)化綁定:

Path:"src/app/app.component.html"

    <label for="example-ngModel">[(ngModel)]:</label>
    <input [(ngModel)]="currentItem.name" id="example-ngModel">

[(ngModel)] 語(yǔ)法只能設(shè)置數(shù)據(jù)綁定屬性。如果你要做得更多,可以編寫(xiě)擴(kuò)展表單。例如,下面的代碼將 <input> 值更改為大寫(xiě):

Path:"src/app/app.component.html"

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

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

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

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

常見(jiàn)的內(nèi)置結(jié)構(gòu)型指令:

  1. NgIf —— 從模板中創(chuàng)建或銷(xiāo)毀子視圖。

你可以通過(guò)將 NgIf 指令應(yīng)用在宿主元素上來(lái)從 DOM 中添加或刪除元素。在此示例中,將指令綁定到了條件表達(dá)式,例如 isActive

Path:"src/app/app.component.html"

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

注:
- 不要忘了 ngIf 前面的星號(hào)(*)。

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

顯示/隱藏與 NgIf

隱藏元素與使用 NgIf 刪除元素不同。為了進(jìn)行比較,下面的示例演示如何使用類(lèi)或樣式綁定來(lái)控制元素的可見(jiàn)性。

Path:"src/app/app.component.html"

    <!-- isSpecial is true -->
    <div [class.hidden]="!isSpecial">Show with class</div>
    <div [class.hidden]="isSpecial">Hide with class</div>


    <p>ItemDetail is in the DOM but hidden</p>
    <app-item-detail [class.hidden]="isSpecial"></app-item-detail>


    <div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
    <div [style.display]="isSpecial ? 'none'  : 'block'">Hide with style</div>

隱藏元素時(shí),該元素及其所有后代仍保留在 DOM 中。這些元素的所有組件都保留在內(nèi)存中,Angular 會(huì)繼續(xù)做變更檢查。它可能會(huì)占用大量計(jì)算資源,并且會(huì)不必要地降低性能。

NgIf 工作方式有所不同。如果 NgIffalse,則 Angular 將從 DOM 中刪除該元素及其后代。這銷(xiāo)毀了它們的組件,釋放了資源,從而帶來(lái)更好的用戶(hù)體驗(yàn)。

如果要隱藏大型組件樹(shù),請(qǐng)考慮使用 NgIf 作為顯示/隱藏的更有效替代方法。

防范空指針錯(cuò)誤

ngIf 另一個(gè)優(yōu)點(diǎn)是你可以使用它來(lái)防范空指針錯(cuò)誤。顯示/隱藏就是最合適的極簡(jiǎn)用例,當(dāng)你需要防范時(shí),請(qǐng)改用 ngIf 代替。如果其中嵌套的表達(dá)式嘗試訪(fǎng)問(wèn) null 的屬性,Angular 將引發(fā)錯(cuò)誤。

下面的例子中 NgIf 保護(hù)著兩個(gè) <div>。僅當(dāng)存在 currentCustomer 時(shí),才會(huì)顯示 currentCustomer 名稱(chēng)。除非它為 null 否則不會(huì)顯示 nullCustomer。

Path:"src/app/app.component.html"

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

Path:"src/app/app.component.html"

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

  1. NgFor —— 為列表中的每個(gè)條目重復(fù)渲染一個(gè)節(jié)點(diǎn)。

NgFor 是一個(gè)重復(fù)器指令 —— 一種用來(lái)顯示條目列表的方法。你定義了一個(gè) HTML 塊,該 HTML 塊定義了應(yīng)如何顯示單個(gè)條目,然后告訴 Angular 以該塊為模板來(lái)渲染列表中的每個(gè)條目。賦值給 *ngFor 的文本是用來(lái)指導(dǎo)重復(fù)器工作過(guò)程的指令。

以下示例顯示了如何將 NgFor 應(yīng)用于簡(jiǎn)單的 <div>。(不要忘了 ngFor 前面的星號(hào)(*)。)

Path:"src/app/app.component.html"

    <div *ngFor="let item of items">{{item.name}}</div>

你還可以將 NgFor 應(yīng)用于組件元素,如以下示例所示。

Path:"src/app/app.component.html"

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

*NGFOR 微語(yǔ)法
&賦值給 *ngFor 的字符串不是模板表達(dá)式。而是一個(gè)微語(yǔ)法 —— 由 Angular 解釋的一種小型語(yǔ)言。字符串 "let item of items" 的意思是:

&&將 items 數(shù)組中的每個(gè)條目存儲(chǔ)在局部循環(huán)變量 item 中,并使其可用于每次迭代的模板 HTML 中。

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

模板輸入變量

item 前面的 let 關(guān)鍵字創(chuàng)建了一個(gè)名為 item 的模板輸入變量。ngFor 指令迭代父組件的 items 屬性所返回的 items 數(shù)組,并在每次迭代期間將 item 設(shè)置為該數(shù)組中的當(dāng)前條目。

ngFor 的宿主元素及其后代中可引用 item,來(lái)訪(fǎng)問(wèn)該條目的屬性。以下示例首先在插值中引用 item,然后把一個(gè)綁定表達(dá)式傳入 <app-item-detail> 組件的 item 屬性。

Path:"src/app/app.component.html"

    <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 屬性在每次迭代中返回該條目的從零開(kāi)始的索引。 你可以在模板輸入變量中捕獲 index,并在模板中使用它。

下面的例子在名為 i 的變量中捕獲 index,并將其與條目名稱(chēng)一起顯示。

Path:"src/app/app.component.ts"

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

*帶 trackBy 的 ngFor**

如果將 NgFor 與大型列表一起使用,則對(duì)某個(gè)條目的較小更改(例如刪除或添加一項(xiàng))就會(huì)觸發(fā)一系列 DOM 操作。 例如,重新查詢(xún)服務(wù)器可能會(huì)重置包含所有新條目對(duì)象的列表,即使先前已顯示這些條目也是如此。在這種情況下,Angular 只能看到由新的對(duì)象引用組成的新列表,它別無(wú)選擇,只能用所有新的 DOM 元素替換舊的 DOM 元素。

你可以使用 trackBy 來(lái)讓它更加高效。向該組件添加一個(gè)方法,該方法返回 NgFor 應(yīng)該跟蹤的值。這個(gè)例子中,該值是英雄的 id。如果 id 已經(jīng)被渲染,Angular 就會(huì)跟蹤它,而不會(huì)重新向服務(wù)器查詢(xún)相同的 id。

Path:"src/app/app.component.html"

    <div *ngFor="let item of items; trackBy: trackByItems">
      ({{item.id}}) {{item.name}}
    </div>

這就是 trackBy 效果的說(shuō)明?!癛eset items” 將創(chuàng)建具有相同 item.id 的新條目?!癈hange ids” 將使用新的 item.id 創(chuàng)建新條目。

如果沒(méi)有 trackBy,這些按鈕都會(huì)觸發(fā)完全的 DOM 元素替換。

有了 trackBy,則只有修改了 id 的按鈕才會(huì)觸發(fā)元素替換。

注:
- 內(nèi)置指令僅僅使用了公共 API。也就是說(shuō),它們沒(méi)有用到任何其它指令無(wú)權(quán)訪(fǎng)問(wèn)的私有 API。

  1. NgSwitch —— 一組在備用視圖之間切換的指令。

NgSwitch 類(lèi)似于 JavaScript switch 語(yǔ)句。它根據(jù)切換條件顯示幾個(gè)可能的元素中的一個(gè)。Angular 只會(huì)將選定的元素放入 DOM。

NgSwitch 實(shí)際上是三個(gè)協(xié)作指令的集合: NgSwitch,NgSwitchCaseNgSwitchDefault,如以下示例所示。

Path:"src/app/app.component.html"

    <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>

NgSwitch 是控制器指令。把它綁定到一個(gè)返回開(kāi)關(guān)值的表達(dá)式,例如 feature。盡管此示例中的 feature 值是字符串,但開(kāi)關(guān)值可以是任何類(lèi)型。

綁定到 [ngSwitch]。如果試圖寫(xiě)成 *ngSwitch,就會(huì)出現(xiàn)錯(cuò)誤,因?yàn)?NgSwitch 是屬性型指令,而不是結(jié)構(gòu)型指令。它不會(huì)直接接觸 DOM,而是會(huì)更改與之相伴的指令的行為。

綁定到 *ngSwitchCase*ngSwitchDefault 、NgSwitchCaseNgSwitchDefault 指令都是結(jié)構(gòu)型指令,因?yàn)樗鼈儠?huì)從 DOM 中添加或移除元素。

  • 當(dāng) NgSwitchCase 的綁定值等于開(kāi)關(guān)值時(shí),就將其元素添加到 DOM 中;否則從 DOM 中刪除。

  • NgSwitchDefault 會(huì)在沒(méi)有任何一個(gè) NgSwitchCase 被選中時(shí)把它所在的元素加入 DOM 中。

開(kāi)關(guān)指令對(duì)于添加和刪除組件元素特別有用。本示例在 "item-switch.components.ts" 文件中定義的四個(gè) item 組件之間切換。每個(gè)組件都有一個(gè)名叫 item 的輸入屬性,它會(huì)綁定到父組件的 currentItem。

開(kāi)關(guān)指令也同樣適用于原生元素和 Web Component。 比如,你可以把 <app-best-item> 分支替換為如下代碼。

Path:"src/app/app.component.html"

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

結(jié)構(gòu)型指令一小節(jié)涵蓋了結(jié)構(gòu)型指令的詳細(xì)內(nèi)容,它解釋了以下內(nèi)容:

  • 為什么在要指令名稱(chēng)前加上星號(hào)(*)。

  • 當(dāng)指令沒(méi)有合適的宿主元素時(shí),使用 <ng-container& 對(duì)元素進(jìn)行分組。

  • 如何寫(xiě)自己的結(jié)構(gòu)型指令。

  • 你只能往一個(gè)元素上應(yīng)用一個(gè)結(jié)構(gòu)型指令。

模板引用變量( #var )

模板引用變量通常是對(duì)模板中 DOM 元素的引用。它還可以引用指令(包含組件)、元素、TemplateRef 或 Web Component。

使用井號(hào)(#)聲明模板引用變量。以下模板引用變量 #phone 會(huì)在 <input> 元素上聲明了一個(gè) phone 變量。

Path:"src/app/app.component.html"

<input #phone placeholder="phone number" />

你可以在組件模板中的任何位置引用模板引用變量。這個(gè)例子中,模板下方的 <button> 就引用了 phone 變量。

Path:"src/app/app.component.html"

<input #phone placeholder="phone number" />


<!-- lots of other elements -->


<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>

模板引用變量如何取得它本身的值

在大多數(shù)情況下,Angular 會(huì)將模板引用變量的值設(shè)置為聲明該變量的元素。在上一個(gè)示例中,phone 指的是電話(huà)號(hào)碼的 <input>。按鈕的單擊處理程序?qū)堰@個(gè) <input> 的值傳給組件的 callPhone() 方法。

NgForm 指令可以更改該行為并將該值設(shè)置為其它值。在以下示例中,模板引用變量 itemForm 出現(xiàn)了 3 次,由 HTML 分隔。

Path:"src/app/app.component.html"

<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
  <label for="name"
    >Name <input class="form-control" name="name" ngModel required />
  </label>
  <button type="submit">Submit</button>
</form>


<div [hidden]="!itemForm.form.valid">
  <p>{{ submitMessage }}</p>
</div>

當(dāng) itemForm 的引用沒(méi)有 "ngForm" 值時(shí),它將是 HTMLFormElement。不過(guò),組件和指令之間的區(qū)別在于,在不指定屬性值的情況下組件將引用自身(隱式引用),而指令不會(huì)更改隱式引用(仍為所在元素)。

但是,帶有 NgForm 時(shí),itemForm 就是對(duì) NgForm 指令的引用,它能夠跟蹤表單中每個(gè)控件的值和有效性。

原生 <form> 元素沒(méi)有 form 屬性,但 NgForm 指令有,這樣就能在 itemForm.form.valid 無(wú)效的情況下禁用提交按鈕,并將整個(gè)表單控制樹(shù)傳給父組件的 onSubmit() 方法。

對(duì)模板引用變量的思考

模板引用變量(#phone)與模板輸入變量(let phone)不同。

模板引用變量的范圍是整個(gè)模板。因此,不要在同一模板中多次定義相同的變量名,因?yàn)樗谶\(yùn)行時(shí)的值將不可預(yù)測(cè)。

替代語(yǔ)法 你也可以用 ref- 前綴代替 #。 下面的例子中就用把 fax 變量聲明成了 ref-fax 而不是 #fax。

Path:"src/app/app.component.html"

<input ref-fax placeholder="fax number" />
<button (click)="callFax(fax.value)">Fax</button>

輸入和輸出屬性

@Input()@Output() 允許 Angular 在其父上下文和子指令或組件之間共享數(shù)據(jù)。@Input() 屬性是可寫(xiě)的,而 @Output() 屬性是可觀察對(duì)象。

考慮以下父子關(guān)系示例:

<parent-component>
  <child-component></child-component>
</parent-component>

在這里,<child-component> 選擇器或子指令嵌入在 <parent-component> 中,用作子級(jí)上下文。

@Input()@Output() 充當(dāng)子組件的 API 或應(yīng)用編程接口,因?yàn)樗鼈冊(cè)试S子組件與父組件進(jìn)行通信??梢园?@Input()@Output() 看做港口或門(mén),@Input() 是進(jìn)入組件的門(mén),允許數(shù)據(jù)流入,而 @Output() 是離開(kāi)組件的門(mén),允許子組件向外發(fā)出數(shù)據(jù)。

關(guān)于 @Input()@Output() 這一部分有其自己的現(xiàn)場(chǎng)演練 / 下載范例。以下小節(jié)將重點(diǎn)介紹示例應(yīng)用程序中的關(guān)鍵點(diǎn)。

@Input() 和 @Output() 是獨(dú)立的

&盡管 @Input()@Output() 通常在應(yīng)用程序中同時(shí)出現(xiàn),但是你可以單獨(dú)使用它們。如果嵌套組件只需要向其父級(jí)發(fā)送數(shù)據(jù),則不需要 @Input(),而只需 @Output()。反之亦然,如果子級(jí)只需要從父級(jí)接收數(shù)據(jù),則只需要 @Input()

如何使用 @Input()

在子組件或指令中使用 @Input() 裝飾器,可以讓 Angular 知道該組件中的屬性可以從其父組件中接收值。這很好記,因?yàn)檫@種數(shù)據(jù)流是從子組件的角度來(lái)看就是輸入。因此,@Input() 允許將數(shù)據(jù)從父組件輸入到子組件中。

為了說(shuō)明 @Input() 的用法,請(qǐng)編輯應(yīng)用程序的以下部分:

  • 子組件類(lèi)及其模板

  • 父組件類(lèi)及其模板

在子組件中

要在子組件類(lèi)中使用 @Input() 裝飾器,請(qǐng)首先導(dǎo)入 Input,然后使用 @Input() 來(lái)裝飾一個(gè)屬性:

Path:"src/app/item-detail/item-detail.component.ts"

import { Component, Input } from '@angular/core'; // First, import Input
export class ItemDetailComponent {
  @Input() item: string; // decorate the property with @Input()
}

在這個(gè)例子中,@Input() 裝飾具有 string 類(lèi)型的屬性 item,但是,@Input() 屬性可以具有任何類(lèi)型,例如 number,string,booleanobjectitem 的值會(huì)來(lái)自下一部分要介紹的父組件。

接下來(lái),在子組件模板中,添加以下內(nèi)容:

Path:"src/app/item-detail/item-detail.component.html"

<p>
  Today's item: {{item}}
</p>

在父組件中

下一步是在父組件的模板中綁定該屬性。在此示例中,父組件模板是 "app.component.html"。

首先,使用子組件的選擇器(這里是 <app-item-detail> )作為父組件模板中的指令。然后,使用屬性綁定將子組件中的屬性綁定到父組件中的屬性。

Path:"src/app/app.component.html"

<app-item-detail [item]="currentItem"></app-item-detail>

接下來(lái),在父組件類(lèi) "app.component.ts" 中,為 currentItem 指定一個(gè)值:

Path:"src/app/app.component.ts"

export class AppComponent {
  currentItem = 'Television';
}

借助 @Input(),Angular 將 currentItem 的值傳給子級(jí),以便該 item 渲染為 Television。

方括號(hào) [] 中的目標(biāo)是子組件中帶有 @Input() 裝飾器的屬性。綁定源(等號(hào)右邊的部分)是父組件要傳給內(nèi)嵌組件的數(shù)據(jù)。

關(guān)鍵是,當(dāng)要在父組件中綁定到子組件中的屬性(即方括號(hào)中的內(nèi)容)時(shí),必須在子組件中使用 @Input()來(lái)裝飾該屬性。

OnChanges 和 @Input()

&要監(jiān)視 @Input() 屬性的更改,請(qǐng)使用 Angular 的生命周期鉤子之一 OnChangesOnChanges 是專(zhuān)門(mén)設(shè)計(jì)用于具有 @Input() 裝飾器的屬性的。

如何使用 @Output()

在子組件或指令中使用 @Output() 裝飾器,允許數(shù)據(jù)從子級(jí)流出到父級(jí)。

通常應(yīng)將 @Output() 屬性初始化為 Angular EventEmitter,并將值作為事件從組件中向外流出。

就像 @Input() 一樣,你也要在子組件的屬性上使用 @Output(),但其類(lèi)型為 EventEmitter。

@Output() 將子組件中的屬性標(biāo)記為一扇門(mén),數(shù)據(jù)可以通過(guò)這扇門(mén)從子組件傳到父組件。 然后,子組件必須引發(fā)一個(gè)事件,以便父組件知道發(fā)生了某些變化。為了引發(fā)事件,@Output() 要和 EventEmitter 配合使用,EventEmitter@angular/core 中的一個(gè)類(lèi),用于發(fā)出自定義事件。

要使用 @Output(),請(qǐng)編輯應(yīng)用程序的以下部分:

  • 子組件類(lèi)及其模板

  • 父組件類(lèi)及其模板

下面的示例演示了如何在子組件中設(shè)置 @Output(),以將你在 HTML 的 <input> 中輸入數(shù)據(jù),并將其追加到父組件中的數(shù)組里。

在子組件中

此示例有一個(gè) <input>,用戶(hù)可以在其中輸入一個(gè)值并單擊引發(fā)事件的 <button>。然后,通過(guò) EventEmitter 將數(shù)據(jù)轉(zhuǎn)給父組件。

首先,請(qǐng)確保在子組件類(lèi)中導(dǎo)入 OutputEventEmitter

import { Output, EventEmitter } from '@angular/core';

接下來(lái),仍然在子組件中,使用組件類(lèi)中的 @Output() 裝飾屬性。下面例子中的 @Output() 名叫 newItemEvent,其類(lèi)型是 EventEmitter,這表示它是一個(gè)事件。

Path:"src/app/item-output/item-output.component.ts"

@Output() newItemEvent = new EventEmitter<string>();

上述聲明的不同之處如下:

  • @Output() —— 一個(gè)裝飾器函數(shù),它將該屬性標(biāo)記為把數(shù)據(jù)從子級(jí)傳遞到父級(jí)的一種方式

  • newItemEvent@Output() 的名字

-EventEmitter<string> — @Output() 的類(lèi)型

  • new EventEmitter<string>() 告訴 Angular 創(chuàng)建一個(gè)新的事件發(fā)射器,并且它發(fā)射的數(shù)據(jù)為 string 類(lèi)型。該類(lèi)型也可以是任何類(lèi)型,例如 number,boolean 等。有關(guān) EventEmitter 的更多信息,請(qǐng)參閱 EventEmitter API 文檔。

接下來(lái),在同一個(gè)組件類(lèi)中創(chuàng)建一個(gè) addNewItem() 方法:

Path:"src/app/item-output/item-output.component.ts"

export class ItemOutputComponent {


  @Output() newItemEvent = new EventEmitter<string>();


  addNewItem(value: string) {
    this.newItemEvent.emit(value);
  }
}

addNewItem() 函數(shù)使用 @Output() newItemEvent 引發(fā)一個(gè)事件,在該事件中它將發(fā)出用戶(hù)鍵入到 <input> 中的內(nèi)容。換句話(huà)說(shuō),當(dāng)用戶(hù)單擊 UI 中的 “Add” 按鈕時(shí),子組件會(huì)讓父組件知道該事件,并將該數(shù)據(jù)傳給父組件。

在子組件的模板中

子組件的模板中有兩個(gè)控件。第一個(gè)是帶有模板引用變量 #newItem 的 HTML <input>,用戶(hù)可在其中鍵入條目名稱(chēng)。用戶(hù)鍵入到 <input> 中的內(nèi)容都存儲(chǔ)在 #newItem 變量中。

Path:"src/app/item-output/item-output.component.html"

<label>Add an item: <input #newItem></label>
<button (click)="addNewItem(newItem.value)">Add to parent's list</button>

第二個(gè)元素是帶有事件綁定的 <button>。之所以知道這是事件綁定,是因?yàn)榈忍?hào)的左側(cè)部分在圓括號(hào)中 (click)。

(click) 事件綁定到子組件類(lèi)中的 addNewItem() 方法,無(wú)論 #newItem 的值如何,該子組件類(lèi)均將其作為參數(shù)。

現(xiàn)在,子組件已經(jīng)有了用于將數(shù)據(jù)發(fā)送到父組件的 @Output() 和引發(fā)事件的方法。下一步是在父組件中。

在父組件中

在此示例中,父組件是 AppComponent,但是你可以使用任何能嵌套子組件的組件。

此示例中的 AppComponent 具有數(shù)組型的 items 列表以及將更多條目添加到數(shù)組中的方法。

Path:"src/app/app.component.ts"

export class AppComponent {
  items = ['item1', 'item2', 'item3', 'item4'];


  addItem(newItem: string) {
    this.items.push(newItem);
  }
}

addItem() 方法接收字符串形式的參數(shù),然后將該字符串添加到 items 數(shù)組中。

在父組件的模板中

接下來(lái),在父組件的模板中,將父組件的方法綁定到子組件的事件。將子組件選擇器(這里是 <app-item-output>)放在父組件的模板 "app.component.html" 中。

Path:"src/app/app.component.html"

<app-item-output (newItemEvent)="addItem($event)"></app-item-output>

事件綁定 (newItemEvent)='addItem($event)'告訴 Angular 將子組件的 newItemEvent 事件連接到父組件中的方法 addItem(),以及將子組件通知父組件的事件作為 addItem() 的參數(shù)。換句話(huà)說(shuō),這是實(shí)際傳遞數(shù)據(jù)的地方。$event 包含用戶(hù)在子模板 UI 中鍵入到 <input> 中的數(shù)據(jù)。

現(xiàn)在,為了查看 @Output() 工作情況,請(qǐng)將以下內(nèi)容添加到父組件的模板中:

<ul>
  <li *ngFor="let item of items">
      {{item}}
  </li>
</ul>

*ngFor 會(huì)遍歷 items 數(shù)組中的條目。當(dāng)你在子組件的 <input> 中輸入值并單擊按鈕時(shí),子組件將發(fā)出事件,父組件的 addItem() 方法將值推送到 items 數(shù)組,并將其渲染在列表中。

@Input() 和 @Output() 在一起

你可以在和下面代碼相同的子組件上使用 @Input()@Output()

Path:"src/app/app.component.html"

<app-input-output [item]="currentItem" (deleteRequest)="crossOffItem($event)"></app-input-output>

目標(biāo) item 是子組件類(lèi)中的 @Input() 屬性,它從父組件的屬性 currentItem 中接收值。當(dāng)你單擊刪除時(shí),子組件將引發(fā)事件 deleteRequest,它攜帶的值將作為父組件的 crossOffItem() 方法的參數(shù)。

下圖是同一子組件上的 @Input()@Output(),并顯示了每個(gè)子組件的不同部分:

如圖所示,像分別使用它們那樣同時(shí)使用輸入和輸出。在這里,子選擇器是 <app-input-output>,其中 itemdeleteRequest 是子組件類(lèi)中的 @Input()@Output() 屬性。屬性 currentItem 和方法 crossOffItem() 都位于父組件類(lèi)中。

要使用“盒子里的香蕉”語(yǔ)法 [()] 組合屬性和事件綁定,請(qǐng)參見(jiàn)雙向綁定。

@Input() 和 @Output() 聲明

你還可以在指令元數(shù)據(jù)的 inputsoutputs 數(shù)組中標(biāo)出這些成員,而不是使用 @Input()@Output() 裝飾器來(lái)聲明輸入和輸出,如本例所示:

Path:"src/app/in-the-metadata/in-the-metadata.component.ts"

// tslint:disable: no-inputs-metadata-property no-outputs-metadata-property
inputs: ['clearanceItem'],
outputs: ['buyEvent']
// tslint:enable: no-inputs-metadata-property no-outputs-metadata-property

固然可以在 @Directive 和 @Component 元數(shù)據(jù)中聲明 inputs 和 outputs,但最好使用 @Input() 和 @Output() 類(lèi)修飾符,如下所示:

Path:"src/app/input-output/input-output.component.ts"

@Input() item: string;
@Output() deleteRequest = new EventEmitter<string>();

如果在嘗試使用輸入或輸出時(shí)收到了模板解析錯(cuò)誤,但是你知道該屬性一定存在,請(qǐng)仔細(xì)檢查你的屬性是否使用 @Input() / @Output() 進(jìn)行了注解,或者是否已在 inputs / outputs 數(shù)組中聲明了它們:

&
Uncaught Error: Template parse errors:
Can't bind to 'item' since it isn't a known property of 'app-item-detail'

為輸入和輸出指定別名

有時(shí),輸入/輸出屬性的公共名稱(chēng)應(yīng)與內(nèi)部名稱(chēng)不同。雖然最好的方法是避免這種情況,但 Angular 確實(shí)提供了一種解決方案。

元數(shù)據(jù)中的別名

要在元數(shù)據(jù)中為輸入和輸出指定別名,請(qǐng)使用冒號(hào)分隔(:)的字符串,其左邊是屬性名,右邊是別名:

Path:"src/app/aliasing/aliasing.component.ts" ··· // tslint:disable: no-inputs-metadata-property no-outputs-metadata-property inputs: ['input1: saveForLaterItem'], // propertyName:alias outputs: ['outputEvent1: saveForLaterEvent'] // tslint:disable: no-inputs-metadata-property no-outputs-metadata-property ···

使用 @Input() / @Output() 裝飾器指定別名

你可以通過(guò)將別名傳給 @Input() / @Output() 裝飾器來(lái)為屬性名指定別名。其內(nèi)部名稱(chēng)保持不變。

Path:"src/app/aliasing/aliasing.component.ts"

@Input('wishListItem') input2: string; //  @Input(alias)
@Output('wishEvent') outputEvent2 = new EventEmitter<string>(); //  @Output(alias) propertyName = ...

模板表達(dá)式中的運(yùn)算符

Angular 模板表達(dá)式的語(yǔ)言是 JavaScript 語(yǔ)法的子集,并為特定情況添加了一些特殊的運(yùn)算符。接下來(lái)將介紹其中的三個(gè)運(yùn)算符:

  1. 管道

在準(zhǔn)備將其用于綁定之前,表達(dá)式的結(jié)果可能需要進(jìn)行一些轉(zhuǎn)換。例如,你可以將數(shù)字顯示為貨幣,將文本更改為大寫(xiě),或過(guò)濾列表并對(duì)其進(jìn)行排序。

管道是簡(jiǎn)單的函數(shù),它們接受輸入值并返回轉(zhuǎn)換后的值。使用管道運(yùn)算符(|),很容易在模板表達(dá)式中使用它們:

Path:"src/app/app.component.html"

    <p>Title through uppercase pipe: {{title | uppercase}}</p>

管道運(yùn)算符會(huì)把它左側(cè)的表達(dá)式結(jié)果傳給它右側(cè)的管道函數(shù)。

還可以通過(guò)多個(gè)管道串聯(lián)表達(dá)式:

Path:"src/app/app.component.html"

    <!-- convert title to uppercase, then to lowercase -->
    <p>Title through a pipe chain: {{title | uppercase | lowercase}}</p>

你還可以對(duì)管道使用參數(shù):

Path:"src/app/app.component.html"

    <!-- pipe with configuration argument => "February 25, 1980" -->
    <p>Manufacture date with date format pipe: {{item.manufactureDate | date:'longDate'}}</p>

json 管道對(duì)調(diào)試綁定特別有用:

Path:"src/app/app.component.html"

    <p>Item json pipe: {{item | json}}</p>

生成的輸出如下所示:

    { "name": "Telephone",
      "manufactureDate": "1980-02-25T05:00:00.000Z",
      "price": 98 }

管道運(yùn)算符的優(yōu)先級(jí)比三元運(yùn)算符( ?: )高,這意味著 a ? b : c | x 將被解析為 a ? b : (c | x)。但是,由于多種原因,如果在 ?: 的第一和第二操作數(shù)中沒(méi)有括號(hào),則不能使用管道運(yùn)算符。一個(gè)較好的做法是在第三個(gè)操作數(shù)中也使用括號(hào)。

  1. 安全導(dǎo)航運(yùn)算符

Angular 安全導(dǎo)航運(yùn)算符 ? 可以對(duì)在屬性路徑中出現(xiàn) nullundefined 值進(jìn)行保護(hù)。在這里,如果 itemnull,它可以防止視圖渲染失敗。

Path:"src/app/app.component.html"

    <p>The item name is: {{item?.name}}</p>

如果 itemnull,則視圖仍然渲染,但顯示的值為空白;你只會(huì)看到 “The item name is:”,后面沒(méi)有任何內(nèi)容。

考慮接下來(lái)這個(gè)帶有 nullItem 的例子。

    The null item name is {{nullItem.name}}

由于沒(méi)有安全導(dǎo)航運(yùn)算符,并且 nullItem 為 null,因此 JavaScript 和 Angular 會(huì)引發(fā)空指針錯(cuò)誤并中斷 Angular 的渲染過(guò)程:

    TypeError: Cannot read property 'name' of null.

但是,有時(shí)在某些情況下,屬性路徑中的 null 值可能是可接受的,尤其是當(dāng)該值開(kāi)始時(shí)為空但數(shù)據(jù)最終會(huì)到達(dá)時(shí)。

使用安全導(dǎo)航運(yùn)算符 ?,當(dāng) Angular 表達(dá)式遇到第一個(gè)空值時(shí),它將停止對(duì)表達(dá)式的求值,并渲染出無(wú)錯(cuò)誤的視圖。

在像 a?.b?.c?.d 這樣的長(zhǎng)屬性路徑中,它工作得很完美。

  1. 非空斷言運(yùn)算符

在 TypeScript 2.0 中,你可以使用 --strictNullChecks 標(biāo)志強(qiáng)制開(kāi)啟嚴(yán)格空值檢查。TypeScript 就會(huì)確保不存在意料之外的 nullundefined。

在這種模式下,有類(lèi)型的變量默認(rèn)是不允許 nullundefined 值的,如果有未賦值的變量,或者試圖把 nullundefined 賦值給不允許為空的變量,類(lèi)型檢查器就會(huì)拋出一個(gè)錯(cuò)誤。

如果無(wú)法在運(yùn)行類(lèi)型檢查器期間確定變量是否 nullundefined,則會(huì)拋出錯(cuò)誤。你可以通過(guò)應(yīng)用后綴非空斷言運(yùn)算符!來(lái)告訴類(lèi)型檢查器不要拋出錯(cuò)誤。

Angular 的非空斷言運(yùn)算符 ! 在 Angular 模板中具有相同的目的。例如,在使用 *ngIf 檢查過(guò) item 是否已定義之后,就可以斷言 item 屬性也已定義。

Path:"src/app/app.component.html"

    <!-- Assert color is defined, even if according to the `Item` type it could be undefined. -->
    <p>The item's color is: {{item.color!.toUpperCase()}}</p>

當(dāng) Angular 編譯器把你的模板轉(zhuǎn)換成 TypeScript 代碼時(shí),它會(huì)防止 TypeScript 不要報(bào)告此 item.color 可能為 nullundefined 的錯(cuò)誤。

與安全導(dǎo)航運(yùn)算符不同的是,非空斷言運(yùn)算符不會(huì)防止出現(xiàn) nullundefined。 它只是告訴 TypeScript 的類(lèi)型檢查器對(duì)特定的屬性表達(dá)式,不做 "嚴(yán)格空值檢測(cè)"。

非空斷言運(yùn)算符 !,是可選的,但在打開(kāi)嚴(yán)格空檢查選項(xiàng)時(shí)必須使用它。

內(nèi)置模板函數(shù)

類(lèi)型轉(zhuǎn)換函數(shù) $any()

有時(shí)候,綁定表達(dá)式可能會(huì)在 AOT 編譯時(shí)報(bào)類(lèi)型錯(cuò)誤,并且它不能或很難指定類(lèi)型。要消除這種報(bào)錯(cuò),你可以使用 $any() 轉(zhuǎn)換函數(shù)來(lái)把表達(dá)式轉(zhuǎn)換成 any 類(lèi)型,范例如下:

Path:"src/app/app.component.html"

<p>The item's undeclared best by date is: {{$any(item).bestByDate}}</p>

當(dāng) Angular 編譯器把模板轉(zhuǎn)換成 TypeScript 代碼時(shí),$any 表達(dá)式可以防止 TypeScript 編譯器在進(jìn)行類(lèi)型檢查時(shí)報(bào)錯(cuò)說(shuō) bestByDate 不是 item 對(duì)象的成員。

$any() 轉(zhuǎn)換函數(shù)可以和 this 聯(lián)合使用,以便訪(fǎng)問(wèn)組件中未聲明過(guò)的成員。

Path:"src/app/app.component.html"

<p>The item's undeclared best by date is: {{$any(this).bestByDate}}</p>

$any() 轉(zhuǎn)換函數(shù)可以用在綁定表達(dá)式中任何可以進(jìn)行方法調(diào)用的地方。

模板中的 SVG

可以將 SVG 用作 Angular 中的有效模板。以下所有模板語(yǔ)法均適用于 SVG 和 HTML。在 SVG 1.1和2.0 規(guī)范中了解更多信息。

為什么要用 SVG 作為模板,而不是簡(jiǎn)單地將其作為圖像添加到應(yīng)用程序中?

當(dāng)你使用 SVG 作為模板時(shí),就可以像 HTML 模板一樣使用指令和綁定。這意味著你將能夠動(dòng)態(tài)生成交互式圖形。

有關(guān)語(yǔ)法示例,請(qǐng)參見(jiàn)下面的示例代碼片段:

Path:"src/app/svg.component.ts"

import { Component } from '@angular/core';


@Component({
  selector: 'app-svg',
  templateUrl: './svg.component.svg',
  styleUrls: ['./svg.component.css']
})
export class SvgComponent {
  fillColor = 'rgb(255, 0, 0)';


  changeColor() {
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    this.fillColor = `rgb(${r}, ${g}, $)`;
  }
}

將以下代碼添加到你的 svg.component.svg 文件中:

Path:"src/app/svg.component.svg"

<svg>
  <g>
    <rect x="0" y="0" width="100" height="100" [attr.fill]="fillColor" (click)="changeColor()" />
    <text x="120" y="50">click the rectangle to change the fill color</text>
  </g>
</svg>

在這里,你可以看到事件綁定語(yǔ)法 click() 和屬性綁定語(yǔ)法([attr.fill]="fillColor")的用法。

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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)