AngularDart4.0 指南- 模板語法二

Class綁定

您可使用Class綁定從元素的類屬性添加和刪除CSS類名稱。html

Class綁定語法相似於屬性(property)綁定。 之前綴類開始,可選地跟一個點(.)和一個CSS類的名字替代括號內的元素屬性:[class.class-name]java

如下示例顯示如何使用class綁定來添加和刪除應用程序的「special」類。 如下是如何設置沒有綁定的屬性:web

<!-- standard class attribute setting  -->
<div class="bad curly special">Bad curly special</div>

你能夠用一個綁定到所需的類名稱的字符串替換它;這是一個全或無,替代綁定。express

<!-- reset/override all class names with a binding  -->
<div class="bad curly special"
     [class]="badCurly">Bad curly</div>

你能夠用一個綁定到所需的類名稱的字符串替換它;這是一個全或無的替代綁定。編程

<!-- reset/override all class names with a binding  -->
<div class="bad curly special"
     [class]="badCurly">Bad curly</div>

最後,你能夠綁定到一個specific類名。 當模板表達式計算結果爲true時,Angular會添加類。 當表達式爲false時,它將刪除類。json

<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>

<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
     [class.special]="!isSpecial">This one is not so special</div>

雖然這是切換單個類名的好方法,可是在同時管理多個類名時一般首選NgClass指令。api

Style綁定

您可使用Style綁定來設置內聯樣式。安全

樣式綁定語法相似於屬性(property)綁定。之前綴樣式開始,後跟一個點(.)和一個CSS樣式屬性的名稱代替括號內的元素屬性,:[style.style-property]服務器

<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>

一些樣式綁定樣式有一個單位擴展名。 如下示例有條件地將字體大小設置爲「em」和「」單位。app

<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>

雖然這是設置單個樣式的好方法,可是在同時設置多個內聯樣式時,一般首選NgStyle指令
請注意,樣式屬性名稱能夠用dash-case(如上所示)或camelCase(如fontSize)書寫。

樣式屬性命名

雖然在AngularDart中camelCasedash-case風格的屬性命名方案是等價的,但只有dash-case命名法才能被dash:html包中CssStyleDeclaration的方法getPropertyValue()setProperty()識別。 所以,使用樣式屬性名稱的dash-case一般是首選。

事件綁定((event))

到目前爲止,您所遇到的綁定指令能夠在一個方向上流動數據:從一個組件到一個元素

用戶不僅是盯着屏幕。 他們在輸入框中輸入文字。 他們從列表中選擇項目。 他們點擊按鈕。 這樣的用戶操做可能致使數據流向相反的方向:從元素到組件

瞭解用戶操做的惟一方法是偵聽某些事件,例如按鍵,鼠標移動,點擊和觸摸。 您經過Angular事件綁定聲明您對用戶操做的興趣。

事件綁定語法由等號左邊括號內的目標事件名稱和右邊帶引號的模板語句組成。 如下事件綁定偵聽按鈕的單擊事件,每當發生點擊時調用組件的onSave()方法:

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

目標事件

圓括號之間的名稱 - 例如(click) - 標識目標事件。 在如下示例中,目標是按鈕的單擊事件。

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

有些人更喜歡使用前綴on-替代方法,稱爲規範形式

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

元素事件多是更常見的目標,但Angular首先查看名稱是否匹配已知指令的事件屬性,以下例所示:

<!-- `myClick` is an event on the custom `ClickDirective` -->
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>

有關別名輸入/輸出屬性的部分將進一步介紹myClick僞指令。

若是名稱未能匹配已知指令的元素事件或輸出屬性,則Angular會報告「未知指令」錯誤。

$event和事件處理語句

在事件綁定中,Angular爲目標事件設置了一個事件處理程序。

事件發生時,處理程序執行模板語句。 模板語句一般包含一個接收器,它響應事件執行一個動做,例如將HTML控件的值存儲到模型中。

綁定經過一個名爲$event事件對象來傳遞關於該事件的信息,包括數據值。

事件對象的形狀由目標事件決定。 若是目標事件是原生DOM元素事件,那麼$event是一個DOM事件對象,具備諸如targettarget.value的屬性。

思考這個例子:

<input [value]="currentHero.name"
       (input)="currentHero.name=$event.target.value" >

此代碼經過綁定到name屬性來設置輸入框value屬性。 要監聽值的更改,代碼會綁定到輸入框的輸入事件。 當用戶進行更改時,將引起輸入事件,綁定在包含DOM事件對象$event的上下文中執行語句。

要更新name屬性,能夠經過路徑$event.target.value來檢索已更改的文本。

若是事件屬於指令(回想組件是指令),則$event具備指令的全部能力。

自定義事件

指令一般使用StreamController來引起自定義事件。 該指令建立一個StreamController並將其stream做爲屬性公開。 該指令調用StreamController.add(payload)來觸發一個事件,傳遞一個消息,能夠是任何東西。 父指令經過綁定監聽此屬性並經過$event對象訪問內容。payload:承載數據

考慮一個呈現英雄信息並響應用戶操做的HeroDetailComponent。 雖然HeroDetailComponent有一個刪除按鈕,但不知道如何刪除英雄自己。 最好的辦法是觸發一個事件,報告用戶的刪除請求。

如下是HeroDetailComponent的相關摘錄:lib/src/hero_detail_component.dart (template)

template: '''
  <div>
    <img src="{{heroImageUrl}}">
    <span [style.text-decoration]="lineThrough">
      {{prefix}} {{hero?.name}}
    </span>
    <button (click)="delete()">Delete</button>
  </div>
''',

lib/src/hero_detail_component.dart (deleteRequest)

final _deleteRequest = new StreamController<Hero>();
@Output()
Stream<Hero> get deleteRequest => _deleteRequest.stream;

void delete() {
  _deleteRequest.add(hero);
}

組件定義了一個私有的StreamController屬性,並經過deleteRequest獲取器公開控制器的stream。 當用戶點擊Delete時,組件的delete()方法被調用,指示StreamControllerHero添加到stream中。

如今想象一個託管的父組件綁定到HeroDetailComponentdeleteRequest事件。

<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>

當觸發deleteRequest事件時,Angular調用父組件的deleteHero方法,傳遞$event變量中的hero-to-delete(HeroDetail發出)。

模板語句有附做用

deleteHero方法有一個附做用:刪除一個英雄。 模板語句的附做用不僅是好的,但可預期。

刪除英雄更新模型,可能會觸發其餘更改,包括查詢並保存到遠程服務器。 這些變化經過系統滲透,並最終顯示在相關視圖。

雙向綁定([(…)])

您常常但願顯示數據屬性,並在用戶進行更改時更新該屬性。

元素另外一方面爲元素更改事件組合設置特定元素屬性和監聽。

Angular爲此提供了一個特殊的雙向數據綁定語法, [(x)] [(x)]語法將屬性綁定的方括號[x]與事件綁定的圓括號(x)組合在一塊兒。

[()] =香蕉盒

在一個盒子裏形象化一個香蕉,記住圓括號在括號內。

當元素有一個名爲x的可設置屬性和一個名爲xChange的對應事件時,[(x)]語法很容易演示。 這是一個適合模式的SizerComponent。 它有一個size值屬性和一個伴隨sizeChange事件:

lib/src/sizer_component.dart

import 'dart:async';
import 'dart:math';
import 'package:angular/angular.dart';
const _minSize = 8;
const _maxSize = _minSize * 5;
@Component(
  selector: 'my-sizer',
  template: '''
    <div>
      <button (click)="dec()" [disabled]="size <= minSize">-</button>
      <button (click)="inc()" [disabled]="size >= maxSize">+</button>
      <label [style.font-size.px]="size">FontSize: {{size}}px</label>
    </div>''',
)
class SizerComponent {
  // TODO: under Angular 4 we will be able to just export the const
  final int minSize = _minSize, maxSize = _maxSize;
  int _size = _minSize * 2;
  int get size => _size;
  @Input()
  void set size(/*String|int*/ val) {
    int z = val is int ? val : int.parse(val, onError: (_) => null);
    if (z != null) _size = min(maxSize, max(minSize, z));
  }
  final _sizeChange = new StreamController<int>();
  @Output()
  Stream<int> get sizeChange => _sizeChange.stream;
  void dec() => resize(-1);
  void inc() => resize(1);
  void resize(int delta) {
    size = size + delta;
    _sizeChange.add(size);
  }
}

初始size是來自屬性綁定的輸入值。 單擊按鈕可在最小/最大值限制內增長或減少size,而後用調整的大小觸發(發出)sizeChange事件。

下面是一個示例,其中AppComponent.fontSizePx是雙向綁定到SizerComponent的:

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

AppComponent.fontSizePx創建初始SizerComponent.size的值。 單擊按鈕經過雙向綁定更新AppComponent.fontSizePx。 修改後的size值流向樣式綁定,使顯示的文本變大或變小。

雙向綁定語法實際上只是屬性(property)綁定和事件綁定的語法糖。 Angular desugars將SizerComponent綁定到這裏:

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

$event變量包含SizerComponent.sizeChange事件的有效數據。 當用戶單擊按鈕時,Angular將$event值分配給AppComponent.fontSizePx

顯然,與單獨的屬性和事件綁定相比,雙向綁定語法至關方便。

使用HTML表單元素(如<input><select>)的雙向綁定會很方便。 可是,沒有原生HTML元素遵循x值和xChange事件模式。

幸運的是,Angular NgModel指令是一個使元素造成雙向綁定的橋樑。

內置指令

早期版本的Angular包含了七十多個內置指令。 社區貢獻了更多,而且爲內部應用程序建立了無數私人指令。

在Angular中你不須要這些指令。 一般,您可使用功能更強大,表現力更強的Angular綁定系統得到相同的結果。 當你能夠寫一個簡單的綁定時爲何要建立一個指令來處理點擊呢?

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

您仍然能夠從簡化複雜任務的指令中受益。 Angular仍然附帶內置的指令; 只是沒有那麼多。 你將會寫你本身的指令,只是很少。

該部分回顧了一些最經常使用的內置指令,歸類爲屬性(attribute)指令結構指令

內置的屬性(attribute)指令

屬性指令監聽並修改其餘HTML元素,屬性(attribute),屬性(property)和組件的行爲。 它們一般應用於元素,就好像它們是HTML屬性同樣,所以也就是名稱。

屬性指令指南中介紹了許多細節。 許多Angular包(如RouterForms包)都定義了本身的屬性指令。 本節介紹最經常使用的屬性指令:

  • NgClass:添加和刪除一組CSS類。
  • NgStyle:添加和刪除一組HTML樣式。
  • NgModel:雙向數據綁定到HTML表單元素。

NgClass

您一般經過動態添加和刪除CSS類來控制元素的顯示方式。 你能夠綁定到ngClass來同時添加或刪除多個類。

class綁定是添加或刪除單個類的好方法。

<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>

要同時添加或刪除許多CSS類,NgClass指令多是更好的選擇。

嘗試綁定ngClass到一個key:value 控制Map。 對象的每一個鍵都是一個CSS類的名字; 若是應該添加類,則其值爲true,若是應該刪除則爲false

考慮一個設置組件屬性的組件方法setCurrentClassescurrentClasses,該對象基於三個其餘組件屬性的true / false狀態添加或刪除三個類:

Map<String, bool> currentClasses = <String, bool>{};
void setCurrentClasses() {
  currentClasses = <String, bool>{
    'saveable': canSave,
    'modified': !isUnchanged,
    'special': isSpecial
  };
}

ngClass屬性綁定添加到currentClasses,相應地設置元素的類:

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

由您決定最初調用setCurrentClasses(),以及什麼時候依賴屬性發生更改。

NgStyle

您能夠根據組件的狀態動態設置內聯樣式。 使用NgStyle,您能夠同時設置多個內聯樣式。
樣式綁定是設置單個樣式值的簡單方法。

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

要同時設置多個內聯樣式,NgStyle指令多是更好的選擇。

嘗試綁定ngStyle到一個key:value控制Map。 對象的每一個鍵都是一個樣式名稱;它的值是適合那種樣式。

考慮一個設置組件屬性的組件方法setCurrentStylescurrentStyles,一個定義了三種樣式的對象,基於另外三個組件屬性的狀態:

Map<String, String> currentStyles = <String, String>{};
void setCurrentStyles() {
  currentStyles = <String, String>{
    'font-style': canSave ? 'italic' : 'normal',
    'font-weight': !isUnchanged ? 'bold' : 'normal',
    'font-size': isSpecial ? '24px' : '12px'
  };
}

添加ngStyle屬性綁定到currentStyles將相應地設置元素的樣式:

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

由您決定最初調用setCurrentStyles(),以及什麼時候依賴屬性發生更改。

NgModel - 與[(ngModel)]造成元素的雙向綁定

在開發數據輸入表單時,一般都會顯示數據屬性,並在用戶進行更改時更新該屬性。

使用NgModel指令進行雙向數據綁定使得這一切變得簡單。 這是一個例子:

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

裏面[(ngModel)]

回顧一下名稱綁定,請注意,您能夠經過單獨綁定<input>元素的value屬性和輸入事件來得到相同的結果。

<input [value]="currentHero.name"
       (input)="currentHero.name=$event.target.value" >

這很麻煩。 誰能夠記住要設置哪一個元素屬性以及哪一個元素事件發出用戶更改? 如何從輸入框中提取當前顯示的文本,以便更新數據屬性? 誰想每一次都看看?

ngModel指令隱藏了本身的ngModel輸入屬性和ngModelChange輸出屬性背後的這些繁重的細節。

<input
  [ngModel]="currentHero.name"
  (ngModelChange)="currentHero.name=$event">

ngModel 數據屬性設置元素的值屬性,ngModelChange事件屬性監聽元素值的變化。

細節是特定於每種元素,所以NgModel指令只適用於ControlValueAccessor支持的元素以使元素適配這個協議。<input>框是其中的一個元素。 Angular爲全部基本的HTML表單元素提供值訪問器,Forms指南展現瞭如何綁定到它們。

您不能將[(ngModel)]應用到非表單原生元素或第三方自定義組件,除非您編寫了一個合適的值存取器,這個技術超出了本指南的範圍。

您不須要爲您編寫的Angular組件添加值存取器,由於您能夠將值和事件屬性命名爲適合Angular基本的雙向綁定語法,並徹底跳過NgModel上面顯示sizer是這種技術的一個例子。

單獨的ngModel綁定是對綁定到元素原生屬性的改進。 你能夠作得更好。

你不該該提到數據屬性兩次。 Angular應該可以捕獲組件的數據屬性,並使用[(ngModel)]語法將其設置爲一個聲明:

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

[(ngModel)]是你須要的嗎? 是否有理由回到擴展的形式?

[(ngModel)]語法只能設置數據綁定屬性。 若是您須要作更多或不一樣的事情,您能夠編寫擴展表單。

如下人爲的例子強制輸入值爲大寫:

<input
  [ngModel]="currentHero.name"
  (ngModelChange)="setUppercaseName($event)">

這裏有全部的變化,包括大寫版本:

內置結構指令

結構指令負責HTML佈局。 它們一般經過添加,刪除和操做它們所鏈接的主機元素來對DOM的結構進行調整或重塑。

結構指令」指南介紹告終構指令的深刻細節,您將在其中學習如下內容:

本節介紹常見的結構指令:

  • NgIf:有條件地從DOM中添加或刪除元素。
  • NgFor:爲列表中的每一個項目重複一個模板。
  • NgSwitch:只顯示多個可能元素中的一個。

NgIf 

您能夠經過向該元素應用NgIf指令(稱爲宿主元素)來添加或移除DOM中的元素。 在此示例中,將指令綁定到條件表達式,如isActive

<hero-detail *ngIf="isActive"></hero-detail>

不要忘記ngIf前面的星號(*)。

非true/false的值

isActive表達式返回true值時,NgIfHeroDetailComponent添加到DOM。 當表達式爲false時,NgIf從DOM中刪除HeroDetailComponent,銷燬該組件及其全部子組件。

在Dart模式下,Dart指望布爾值(類型爲bool的)爲truefalse。 即便在生產模式中,Dart惟一真實的是true, 全部其它值是false。 另外一方面,TypeScript和JavaScript將許多值(包括非空對象)視爲true。 例如,TypeScript Angular程序一般具備諸如* ngIf =「currentHero」的代碼,其中Dart程序具備諸如* ngIf =「currentHero!= null」之類的代碼。

將TypeScript代碼轉換爲Dart代碼時,請注意真/假問題。 例如,忘記!= null可能會致使檢查模式中的異常,例如「EXCEPTION:type ‘Hero’ is not a subtype of type ‘bool’ of ‘boolean expression’」. 有關更多信息,請參閱Dart語言導覽中的布爾值

Dart 2.0注意:檢查模式不會在飛鏢2.0。 有關更多信息,請參閱Dart 2.0更新

顯示/隱藏不是一回事

您可使用類或樣式綁定來控制元素的可見性:

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

<!-- HeroDetail is in the DOM but hidden -->
<hero-detail [class.hidden]="isSpecial"></hero-detail>

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

隱藏一個元素與用NgIf去除一個元素是徹底不一樣的。

當你隱藏一個元素時,該元素及其全部的後代仍然保留在DOM中。 這些元素的全部組件都保留在內存中,Angular可能會繼續檢查更改。 您的應用可能會佔用至關可觀的計算資源,會下降用戶不可見的性能。

NgIffalse時,Angular從DOM中刪除元素及其後代。 它摧毀了他們的組件,潛在地釋放了大量的資源,從而帶來了更加快速的用戶體驗。

展現/隱藏技術適合少數幾個後代的元素。 警戒隱藏大型組件樹; NgIf多是更安全的選擇。

警戒null

ngIf指令一般用於防止null。 顯示/隱藏是無用的。 若是嵌套表達式試圖訪問null屬性,Angular會拋出一個錯誤。

這裏咱們看到NgIf守護兩個<div>currentHero名稱僅在有currentHero時出現。 nullHero從不顯示。

<div *ngIf="currentHero != null">Hello, {{currentHero.name}}</div>
<div *ngIf="nullHero != null">Hello, {{nullHero.name}}</div>

另請參閱下面描述的安全導航操做符

NgFor

NgFor是一個迭代指令 - 一種呈現項目列表的方式。 您能夠定義一個HTML塊來定義應該如何顯示單個項目。 您告訴Angular將該塊用做呈現列表中每一個項目的模板。

下面是NgFor應用於<div>的例子:

<div *ngFor="let hero of heroes">{{hero.name}}</div>

您也能夠將NgFor應用於組件元素,以下例所示:

<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>

不要忘記ngFor前面的星號(*)。

分配給* ngFor的文本是指導迭代器進程的指令。

*ngFor微語法

分配給* ngFor的字符串不是模板表達式。 這是一種微語法 - Angular解釋的一種小語言。 字符串「let hero of heroes」是指:

取英雄列表中的每一個英雄,將其存儲在本地英雄循環變量中,並使其可用於每次迭代的模板HTML。

Angular把這條指令翻譯成一個圍繞宿主元素的<template>,而後重複使用這個模板爲列表中的每一個英雄建立一組新的元素和綁定。

在「結構指令」指南中瞭解微語法。

模板輸入變量

hero以前的let關鍵字建立一個名爲hero的模板輸入變量。 ngFor指令迭代由父組件的heroes屬性返回的heroes,並在每次迭代期間將hero設置爲列表中的當前項目。

要訪問hero的屬性,請參考ngFor宿主元素(或其後代內)中的hero輸入變量。在這裏,英雄首先在插值中被引用,而後傳遞給<hero-detail>組件的hero屬性綁定。

<div *ngFor="let hero of heroes">{{hero.name}}</div>
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>

在「結構指令」指南中瞭解有關模板輸入變量的更多信息。

* ngFor與index(索引)

NgFor指令上下文的index屬性返回每一個迭代中項目的從零開始的索引。 您能夠捕獲模板輸入變量中的index,並在模板中使用它。

下一個示例捕獲名爲i的變量中的索引,並使用像這樣的英雄名稱來顯示它。

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

瞭解其餘NgFor上下文值,例如NgFor API參考中的lastevenodd

*ngFor和trackBy

NgFor指令可能表現不佳,特別是在大型列表中。 對一個項目,刪除項目或添加項目的小改動能夠觸發DOM操做的級聯。

例如,從新查詢服務器可能會重置全部新的英雄對象的列表。

大多數,若是不是所有,之前顯示的英雄。 你知道這一點,由於每一個英雄的ID沒有改變。 可是Angular只能看到新的對象引用列表。 它別無選擇,只能拆除舊的DOM元素並插入全部新的DOM元素。

Angular能夠經過trackBy避免這種流失。 向組件添加一個返回NgFor應跟蹤值的方法。 在這個例子中,這個值就是英雄的ID

int trackByHeroes(int index, Hero hero) => hero.id;

在微語法表達式中,將trackBy設置爲此方法。

<div *ngFor="let hero of heroes; trackBy: trackByHeroes">
  ({{hero.id}}) {{hero.name}}
</div>

這是trackBy效果的一個例子。 「Reset heroes」用相同的 hero.ids建立新的heroes。 「Change ids」用新的hero.ids創造新的heroes 。

  • 沒有trackBy,這兩個按鈕都會觸發完整的DOM元素替換。
  • 有了trackBy,只有更改id觸發器元素替換。

NgSwitch指令

NgSwitch就像Dart switch語句。 它能夠根據切換條件從幾個可能的元素中顯示一個元素。 Angular只把選中的元素放入DOM中。

NgSwitch其實是一組三個協做指令:NgSwitchNgSwitchCaseNgSwitchDefault,如本例所示。

<div [ngSwitch]="currentHero.emotion">
  <happy-hero    *ngSwitchCase="'happy'"    [hero]="currentHero"></happy-hero>
  <sad-hero      *ngSwitchCase="'sad'"      [hero]="currentHero"></sad-hero>
  <confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></confused-hero>
  <unknown-hero  *ngSwitchDefault           [hero]="currentHero"></unknown-hero>
</div>

您可能會遇到舊代碼中的NgSwitchWhen指令。 這是NgSwitchCase的棄用名稱。

NgSwitch是控制器指令。將其綁定到返回switch值的表達式。本例中的emotion值是一個字符串,可是switch值能夠是任何類型。

綁定到[ngSwitch]。 若是您嘗試設置*ngSwitch,則會出現錯誤,由於NgSwitch是一個屬性指令,而不是結構指令。 它改變了其同伴指令的行爲。 它不直接操做DOM。

綁定到*ngSwitchCase*ngSwitchDefaultNgSwitchCaseNgSwitchDefault指令是結構指令,由於它們添加或刪除DOM中的元素。

  • NgSwitchCase在其綁定值等於交換機值時將其元素添加到DOM。
  • 當沒有選擇NgSwitchCase時,NgSwitchDefault將其元素添加到DOM。

switch指令對於添加和刪除組件元素特別有用。本示例在hero_switch_components.dart文件中定義的四個「emotional hero」組件之間進行切換。每一個組件都有一個綁定到父組件的currentHero的英雄輸入屬性。

switch指令也適用於原生元素和Web組件。 例如,您可使用如下代替<confused-hero>switch選項。

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

模板引用變量(#var)

模板引用變量一般是對模板內DOM元素的引用。 它也能夠是對Angular組件或指令或Web組件的引用。

使用hash符號()來聲明一個引用變量。 #phone<input>元素上聲明瞭一個phone變量。

<input #phone placeholder="phone number">

您能夠參考模板中任何位置的模板引用變量。 在<input>上聲明的phone變量在模板另外一端的<button>中被使用

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

如何得到引用變量的值

在大多數狀況下,Angular將引用變量的值設置爲聲明的元素。在前面的例子中, phone是指電話號碼 <input>框。電話按鈕點擊處理程序將輸入值傳遞給組件的callPhone方法。可是一個指令能夠改變這種行爲,並將其值設置爲別的東西,好比自己。 NgForm指令這樣作。

如下是Forms指南中表單示例的簡化版本。

<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
    <div class="form-group">
        <label for="name">Name
            <input class="form-control"
                   ngControl="name"
                   required
                   [(ngModel)]="hero.name">
        </label>
    </div>
    <button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
</form>
<div [hidden]="!heroForm.form.valid">
    {{submitMessage}}
</div>

在這個例子中,一個模板引用變量heroForm出現三次,被大量的HTML隔開。heroForm的值是什麼? heroForm是一個Angular NgForm指令的引用,能夠跟蹤表單中每一個控件的值和有效性。

原生<form>元素沒有form屬性。 可是NgForm指令有,它解釋了若是heroForm.form.valid無效而且將整個表單控件樹傳遞給父組件的onSubmit方法,您能夠禁用提交按鈕。

模板引用變量警告說明

模板引用變量(#phone)與模板輸入變量(let phone)不一樣,如您在*ngFor中可能看到的那樣。 瞭解「結構指令」指南中的差別。

引用變量的範圍是整個模板。 不要在同一模板中屢次定義相同的變量名稱。 運行時值將是不可預知的。

你可使用ref-前綴替代。 本示例將fax變量聲明爲ref-fax,而不是#fax

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

輸入和輸出屬性(@Input和@Output)

到目前爲止,該頁面主要集中在綁定到模板表達式中的組件成員以及出如今綁定聲明右側的語句上。
該位置的成員是數據綁定

本節重點討論對目標的綁定,它們是綁定聲明左側的指令屬性。這些指令屬性必須聲明爲輸入輸出

請記住:全部組件都是指令。

請注意數據綁定目標和數據綁定之間的重要區別。

綁定的目標是在=的左邊。 源位於=的右側。

綁定的目標是綁定標點符號中的屬性或事件:[],()[()]。 源是在引號(「」)內或插值({{}})內。

source指令的每一個成員均可以自動得到綁定。 您沒必要在模板表達式或語句中使用任何特殊的操做來訪問指令成員。

您對目標指令的成員訪問權限有限。 您只能綁定到明確標識爲輸入和輸出的屬性。

在下面的代碼片斷中,iconUrlonSaveAppComponent的數據綁定成員,而且在等號(=)右側的引用語法中被引用。

<img [src]="iconUrl"/>
<button (click)="onSave()">Save</button>

它們既不是輸入也不是輸出。 他們是綁定的來源。 目標是本地的<img><button>元素。

如今看另外一個片斷,其中HeroDetailComponentequals=)左邊綁定的目標。

<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
</hero-detail>

HeroDetailComponent.heroHeroDetailComponent.deleteRequest都在綁定聲明的左側。 HeroDetailComponent.hero在括號內; 它是一個屬性綁定的目標. HeroDetailComponent.deleteRequest在括號內; 它是事件綁定的目標。

聲明輸入和輸出屬性

目標屬性必須明確標記爲輸入或輸出。

HeroDetailComponent中,這些屬性使用註解標記爲輸入或輸出屬性。

@Input()
Hero hero;
final _deleteRequest = new StreamController<Hero>();
@Output()
Stream<Hero> get deleteRequest => _deleteRequest.stream;

輸入仍是輸出?

input屬性一般接收數據值。 Output屬性公開事件生成器,如Stream對象。

術語inputOutput反映了目標指令的視角。

HeroDetailComponent.heroHeroDetailComponent角度的輸入屬性,由於數據從模板綁定表達式流入該屬性。

HeroDetailComponent.deleteRequest是從HeroDetailComponent角度來看的一個輸出屬性,由於在模板綁定語句中,事件流出該屬性並處理該處理程序。

別名輸入/輸出屬性

有時輸入/輸出屬性的公共名稱應與內部名稱不一樣。

屬性指令一般是這種狀況。指令消費者但願綁定到指令的名稱。 例如,當您使用myClick選擇器將指令應用於<div>標記時,您但願綁定到的事件屬性也稱爲myClick

<div (myClick)="clickMessage=$event" clickable>click with myClick</div>

可是,指令名稱是指令類中屬性名稱一般來講是一個糟糕的選擇。 指令名不多描述屬性的做用。 myClick指令名稱對於發出點擊消息的屬性不是一個好名字。

幸運的是,您能夠建立符合常規指望的屬性的公共名稱,同時在內部使用不一樣的名稱。 在上面的示例中,代碼經過myClick別名綁定到指令本身的click屬性。

要爲屬性名稱指定別名,請將別名傳遞到輸入/輸出裝飾器,以下所示:

final _onClick = new StreamController<String>();
// @Output(alias) propertyName = ...
@Output('myClick')
Stream<String> get clicks => _onClick.stream;

模板表達式運算符

模板表達式語言使用Dart語法的一個子集,輔以幾個特殊的運算符,用於特定的場景。接下來的部分將介紹這些操做符中的兩個:管道安全導航操做符。

管道操做符(|)

在準備使用綁定以前,表達式的結果可能須要進行一些轉換。 例如,您能夠將數字顯示爲貨幣,強制文本爲大寫,或篩選列表並對其進行排序。

對於這些小型轉換來講,Angular 管道是一個很好的選擇。 管道是簡單的函數,它接受一個輸入值並返回一個轉換後的值。 使用管道運算符|),它們很容易在模板表達式中應用:

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

管道運算符將左邊表達式的結果傳遞給右邊的管道函數。

您能夠經過多個管道連接表達式:

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

您也能夠將參數應用於管道:

<!-- pipe with configuration argument => "February 25, 1970" -->
<div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div>

json管道能夠幫助調試綁定:

<div>{{currentHero | json}}</div>

生成的輸出以下所示:

{ "id": 0, "name": "Hercules", "emotion": "happy",
  "birthdate": "1970-02-25T08:00:00.000Z",
  "url": "http://www.imdb.com/title/tt0065832/",
  "rate": 325 }

安全導航運算符(?.)和null屬性路徑

Angular安全導航運算符(?.)與Dart條件成員訪問運算符同樣,是防止屬性路徑中的空值的便利方法。 在這裏,若是currentHero爲空,則防止視圖呈現失敗。

The current hero's name is {{currentHero?.name}}

當如下數據綁定title屬性爲null時會發生什麼?

The title is {{title}}

視圖仍然呈現,但顯示的值是空白的; 你只看到「The title is」沒有任何東西。 這是合理的行爲。 至少該應用程序不會崩潰。

假設模板表達式涉及一個屬性路徑,就像在下一個例子中顯示一個空的英雄的name同樣。

The null hero's name is {{nullHero.name}}

Dart拋出異常,Angular也拋出異常:

EXCEPTION: The null object does not have a getter 'name'.

更糟的是,整個視圖消失。

若是hero屬性不能爲空,這將是合理的行爲。 若是它永遠不能爲空,但它是空的,這是一個應該被捕獲和修復的編程錯誤。 拋出異常是正確的。

另外一方面,屬性路徑中空值時不時出現可能還好,特別是當數據如今爲空,未來將返回數據。

在等待數據的時候,視圖應該沒有怨言地呈現,而null屬性路徑應該像title屬性同樣顯示爲空白。

不幸的是,當currentHero爲空時,應用程序崩潰。

你能夠用*ngIf來解決這個問題。

<!--No hero, div not displayed, no error -->
<div *ngIf="nullHero != null">The null hero's name is {{nullHero.name}}</div>

這些方法有好處,但可能會是累贅,特別是若是屬性路徑很長。 想象一下,在諸如a.b.c.d這樣的長屬性路徑中的某個地方防止空值。

Angular安全導航操做符(?.)是一種更爲流暢和方便的方法來防止在屬性路徑中出現空。表達式在達到第一個空值時會被釋放。 顯示器是空白的,但應用程序保持滾動沒有錯誤。

<!-- No hero, no problem! -->
The null hero's name is {{nullHero?.name}}

它適用於很長的屬性路徑,如a?.b?.c?.d

概要

您已經完成了對模板語法的概覽。 如今是時候把這些知識應用到你本身的組件和指令上。

相關文章
相關標籤/搜索