Template Syntax
在上一篇的Angular架構中有提到,透過模版語法,template可以與component做許多的溝通。
那這一篇就會介紹Angular內建的模版語法參考。
對於tempalte.html來說,所有的html標籤都可以使用,除了<script>以外,這是為了維護模版的安全性,去除template被攻擊的風險,因此在template中所有的<script>會被忽略,並跳出警告。
接下來我們來介紹所有Angular的模板語法
{{…}}
以下為一個範例
<h3> {{title}} <img src="{{heroImageUrl}}" style="height:30px"> </h3>
大括號之間值通常是組件屬性的名稱。Angular使用相應組件屬性的字符串值替換該名稱。
在上面的例子中,Angular會取元件裡title和heroImageUrl的屬性,並會取代{{title}}及{{heroImageUrl}},因此在頁面上會顯示一個大的應用程序標題,然後一個英雄影像的位置。
在{{…}}之間,我們也可以使用模板表達式去轉換要顯示的值。
例如在刮弧中做運算:
<!-- "The sum of 1 + 1 is 2" --> <p>The sum of 1 + 1 is {{1 + 1}}</p>
或者也可以呼叫component的function getVal():
<!-- "The sum of 1 + 1 is not 4" --> <p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
大多的運算符都可以用在表達式裡面,除了一些會影響到component的值的運算符,如=、+=、-=之類。
有時候{{…}}裡面要綁定的數值也可以是在template定義的(使用#
符號),請見下面的範例
<div *ngFor="let hero of heroes">{{hero.name}}</div> <input #heroInput> {{heroInput.value}}
「註」:如果在元件裡已經有變數名稱叫做hero,而template裡又有一個hero,在template會優先使用template內定義的變數。
在表達式中不能引用任何除了undefined外的全域變數,如window或document,也不能用consolo.log,只能使用元件內的屬性或方法,或者template裡上下文內的成員
在使用{{…}}時,有下面四個原則要注意:
- No visible side effects:不應該改變任何元件內的值,在rendering整個表式示時應該是穩定的
- Quick execution:表達式的運算應該要很快,因為它會在許多狀況下被呼叫,因此若是裡面含有許多複雜運算時,請考慮用快取以增加效能。
- Simplicity:雖然可以在{{…}}裡面寫很複雜的運算但是不建議,最多在裡面使用!符號,不然還是建議將運算放到元件內去計算,以利閱讀及開發
- Idempotent: idempotent的意思是如果相同的操作再執行第二遍第三遍,結果還是跟第一遍的結果一樣 (也就是說不管執行幾次,結果都跟只有執行一次一樣)。
(event)=”statement”
(event)=”statement”是Template statements。事件綁定是利用這樣的方式去做的,下面是一個範例:
<button (click)="deleteHero()">Delete hero</button>
與{{…}}不同的是,在這樣的語法中,是可以去改變元件的值的,並且被改變的值會透過單向綁定{{…}}顯示在畫面上。因此,(event)=”statement”的statement是能夠有支援=運算符的,但是+=、-=和++、–是不被允許的。
語句上下文也可以引用模板自己的上下文的屬性。在以下範例中,將模版$event
對象,模板輸入變量(let hero)和模板引用變量(#heroForm)傳遞給組件的事件處理方法。
下面是一個範例
<button (click)="onSave($event)">Save</button> <button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
[target]=”statement”
以下是一個範例:
<!-- Bind button disabled state to `isUnchanged` property --> <button [disabled]="isUnchanged">Save</button>
這樣的語法,能夠讓isUnchanged為true時,顯示這樣的畫面
<button disabled>Save</button>
綁定方法整理
資料方向 | 語法 | 類型 |
---|---|---|
單向綁定 從資料源到view |
{{expression}} [target]="expression" bind-target="expression" |
Interpolation Property Attribute Class Style |
單向綁定 從view的目標到資料源 |
(target)="statement" on-target="statement" |
Event |
雙向綁定 |
[(target)]="expression" bindon-target="expression" |
Two-way |
HTML attribute與DOM property
HTML attribute和DOM property(物件屬性)的區別對於理解Angular綁定是很重要的。一旦使用插值(`{{…}}`),就不是使用HTML attribute,而是在設置DOM property。
一些HTML attribute可以1:1的對應到一個DOM property,例如:id
。
一些HTML attribute沒有相應的DOM property,例如:colspan
(無法使用插值)。
一些DOM property沒有相應的HTML attribute,例如:textContent
。
許多DOM property似乎對應到HTML attribute…但不是以你想像的方式!
HTML attribute的value指定初始值; DOM property的value屬性是當前值。
例如,當瀏覽器執行下面HTML,它創建一個對應的DOM節點,其value屬性初始化為“Bob”。
<input type="text" value="Bob">
當用戶在輸入框中輸入“Sally”時,DOM property的value變成“Sally”。
但是,HTML attribute的value保持不變,如下所示:
input.getAttribute('value'); //取的的值還是返回“Bob”
disabled屬性是另一個特殊的例子。按鈕的disabled 屬性是false默認的,所以按鈕被啟用。當你添加disabled 屬性時,它的存在會初始化按鈕的disabled 屬性,true所以按鈕被禁用。添加和刪除disabled屬性禁用並啟用按鈕。屬性的值是不相關的,這就是為什麼你不能用下面的語法來將按鈕設為enable。
<button disabled="false">Still Disabled</button>
因此,HTML attribute和DOM property是不一樣的,即使它們具有相同的名稱。
綁定目標整理
類型 | 目標 | 例子 |
---|---|---|
屬性 | 元素屬性 組件屬性 指令屬性 |
<img [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"></app-hero-detail> <div [ngClass]="{'special': isSpecial}"></div> |
事件 | 元素事件 組件事件 指令事件 |
<button (click)="onSave()">Save</button> <app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail> <div (myClick)="clicked=$event" clickable>click me</div> |
雙向 | 事件和財產 |
<input [(ngModel)]="name"> |
屬性 | 屬性(例外) |
<button [attr.aria-label]="help">help</button> |
類 | class 屬性 |
<div [class.special]="isSpecial">Special</div> |
樣式 | style 屬性 |
<button [style.color]="isSpecial ? 'red' : 'green'"> |
Property binding or interpolation?
屬性綁定與插值常常能達到相同的功效,如下面的範例:
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p> <p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p> <p><span>"{{title}}" is the <i>interpolated</i> title.</span></p> <p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
一般而言,為了易讀性,會使用插值({{…}})。但當沒有要綁定的元素屬性時,必須使用屬性綁定。例如:
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
會得到這個錯誤
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
這是因為插值只能設定properties而不能設定attributes。
這時可以改成
<table border=1> <!-- expression calculates colspan=2 --> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> <!-- ERROR: There is no `colspan` property to set! <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> --> <tr><td>Five</td><td>Six</td></tr> </table>
則可以正常顯示如下圖
Built-in structural directives
常見的結構指令如下:
- NgIf – 有條件地從DOM中添加或刪除一個元素,要注意,這和css的show、hide不一樣,當元素被dom移除時,是沒有辦法去操作DOM元素裡的物件的。
<app-hero-detail *ngIf="isActive"></app-hero-detail>
- NgSwitch – 一組在不同視圖之間切換的指令
<div [ngSwitch]="currentHero.emotion"> <app-happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></app-happy-hero> <app-sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></app-sad-hero> <app-confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></app-confused-hero> <app-unknown-hero *ngSwitchDefault [hero]="currentHero"></app-unknown-hero> </div>
- NgForOf – 為列表中的每個項目重複一個模板
<app-hero-detail *ngFor="let hero of heroes" [hero]="hero"></app-hero-detail>
Template reference variables ( #var )
在Angular也可以使用#開頭來將使用者在網頁上input輸入的值轉為一個變數,如下:
<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>
這個功能在做表單驗證時非常的方便
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm"> <div class="form-group"> <label for="name">Name <input class="form-control" name="name" required [(ngModel)]="hero.name"> </label> </div> <button type="submit" [disabled]="!heroForm.form.valid">Submit</button> </form> <div [hidden]="!heroForm.form.valid"> {{submitMessage}} </div>
允許外部元件讀取元件內的屬性
要讓元件內的屬性能夠給其他元件使用,或者讀取其他元件的屬性,可以在component.ts內宣告
@Input() hero: Hero; @Output() deleteRequest = new EventEmitter<hero>();
或者這樣也可以
@Component({ inputs: ['hero'], outputs: ['deleteRequest'], })
輸入屬性通常接收數據值。 輸出屬性會發送事件,如EventEmitter。
下面的圖顯示元件屬性的輸入和輸出的範例。
safe navigation operator ( ?. )
為了防止出現null reference exception,我們可以使用?.,當值為空值時,會直接傳回空白,可以避免產生不必要的exception
以下為範例
The current hero's name is {{currentHero?.name}}
參考資料
完整綁定範例資料請見此:線上範例