重拾Angular(二)模板語法

原文連接html

Angular官方開發文檔講的很細緻,可是這是一把雙刃劍。

雙刃劍

上一篇中Angular CLI的文檔,並不是Angular官方開發文檔的一部分。(可能我沒發現吧)
而從這篇文章開始,我將開始閱讀Angular的官方開發文檔vue

首先,我閱讀完了教程核心知識-構架,粗略的瞭解下Angular的開發流程和一些基本的概念。node

而後,就是看核心知識-組件與模板了,看完了第一遍其實,仍是有點懵。express

有幾點感覺:segmentfault

  1. 多是由於是英譯,有些地方感受翻譯的有點不夠接地氣,不太便於國人理解
  2. 文字過於冗長,有時候讓人很難抓到重點
  3. 像看小說,必定要按部就班慢慢看,否則稍微跳一下,劇情就接不上了
  4. 看過的vue文檔的朋友,必定會感受vue的文檔簡潔而清晰

因此在看Angular文檔的同時,提煉總結下,仍是頗有必要的。api

1.Attribute & Property

官方文檔中強調: 定模板綁是經過 property 和事件來工做的,而不是 attribute。

在Angular開發中,我的感受必定要明確這二者的別,才能更好的理解Angular的工做方式。
否則很容易產生混淆,所以我把這一小節放到最前面。數組

attribute是指HTML attribute
property是指DOM property瀏覽器

Angular世界中所說的【屬性】,基本上應該是指【property】安全

attribute在 Angular 中 惟一的做用是用來初始化元素和指令的狀態。 當進行數據綁定時,只是在與元素和指令的 property 和事件打交道,而 attribute 就徹底靠邊站了。app

2.插值

2.1.文本

<p>My current hero is {{currentHero.name}}</p>

2.2.原始Html

Angular 數據綁定對危險 HTML 有防備。 在顯示它們以前,它會對內容先進行處理
不論是插值表達式仍是屬性綁定,都不會容許帶有 script 標籤的 HTML 泄漏到瀏覽器中。

src/app/app.component.ts

htmlWithEvil = '<h1>be careful</h1><br><script>alert("evil never sleeps")</script>';

src/app/app.component.html

<!-- 方式1 -->
<h1>{{htmlWithEvil}}</h1>
<!-- 方式2 -->
<h1 [innerHTML]="htmlWithEvil"></h1>

方式1:直接插值,顯然Angular不會這麼輕易讓你直接插html進來。htmlWithEvil會被轉義後輸出,html不會有任何效果

方式2:綁定innerHTML屬性,htmlWithEvil中的<script>被過濾後,html生效

2.3.模板表達式

指的是{{expression}} 中的 expression

方便起見本文中的expression均是指模板表達式

expression不只能夠在花括號裏面用,能夠在屬性綁定的時候用

expression的語法和JavaScript類似,但不徹底相同

與JavaScript的區別
expression不予許出現:

  • 賦值 (=, +=, -=, ...)
  • new 運算符
  • 使用 ; 或 , 的鏈式表達式
  • 自增和自減運算符:++ 和 --
  • 不支持位運算 | 和 &

2.4.模板表達式運算符

模板表達式運算符是JavaScript中沒有的的特性

2.4.1.管道操做符

管道操做符|,可對錶達式結果進行一些轉換

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

想了解更多Angular自帶的管道,能夠官網查管道的API

2.4.2.安全導航操做符和空屬性路徑

能夠完美的解決空值異常,增長視圖的容錯性。

{{order?.consignee?.mobile}}

即便consignee是個空值,也不會報異常,只是不顯示而已能夠完美的解決空值異常,增長視圖的容錯性。

2.4.3.非空斷言操做符

若是你打開了嚴格檢測,那就要用到這個模板操做符,而其它狀況下則是可選的

<!--No hero, no text -->
<div *ngIf="hero">
  The hero's name is {{hero!.name}}
</div>
在 TypeScript 2.0 中,你可使用 --strictNullChecks 標誌強制開啓嚴格空值檢查。TypeScript 就會確保不存在乎料以外的 null 或 undefined。

3.屬性綁定

3.1.基本語法

[property]="expression"

3.1.三種寫法

<!-- 例1 -->
<img [src]="heroImageUrl">
<!-- 例2 -->
<img bind-src="heroImageUrl">
<!-- 例3 -->
<img src="{{heroImageUrl}}">
  • 以上三種方式,效果是相同的
  • 例1例2,都是屬於純正的屬性綁定語法
  • 例3,血統不純正,不推薦使用
    估計是爲了兼顧AngularJs的使用習慣
    雖然在渲染視圖以前,Angular會把例3翻譯成相應的屬性綁定
    但當property數據類型不是字符串時,會產生問題。後面3.2舉例
上面這個例子中, imgattributesrc與 imagepropertysrc相同,全部感受讓人產生混淆

3.2.容易混淆之處

<!-- 例1 -->
<span [innerHTML]="htmlWithEvil"></span>
<!-- 例2 -->
<span innerHTML="{{htmlWithEvil}}"></span>

二者效果徹底相同,再次印證:方括號裏面的實際上是property,而不是,不是,不是attribute

<button [disabled]="isDisabled">Save</button>
<button bind-disabled="isDisabled">Save</button>
<button disabled="{{isDisabled}}">Save</button>
  • 若是當isDisabled值爲true的時候,三個按鈕都禁用了,沒毛病。
  • 若是當isDisabled值爲false的時候,前兩個按鈕可用了,沒毛病。可是!第三個按鈕依然仍是禁用
    應該是把false轉成'false'後,賦值給了disabled這個property
    因此,不要覺得你用TypeScript在寫Angular,就能夠忘了JavaScript這個弱類型語言。
最終建議:爲了不沒必要要的麻煩,仍是使用 標準語法爲好.

3.3.組件屬性

屬性綁定,一樣適用於自定義組件。不過須要注意的是,在綁定屬性時,務必傳入匹配的數據類型

<app-hero-detail [hero]="currentHero"></app-hero-detail>
app-hero-detail組件的 hero 屬性想要一個 Hero 對象,那就在屬性綁定中精確地給它一個 Hero 對象

4.attribute綁定

基本語法:[attr.colspan]="expression"

<table>
  <tr>
    <td>1</td>
    <td>2</td>
  </tr>
  <tr>
    <td [attr.colspan]="1 + 1">One-Two</td>
  </tr>
</table>

當有些元素的attribute並無對應property時,就能夠這麼幹

考慮 ARIA, SVG 和 table 中的 colspan/rowspan 等 attribute。 它們是純粹的 attribute,沒有對應的屬性可供綁定。

5.class綁定

5.1.基本語法

[class.class-name]="isSpecial",isSpecial值爲 true/false

追加

<div class="a-class b-class" [class.c-class]='condition'>xxx</div>

condition = true時,會在原有class基礎上,追加c-class。實際輸出:

<div class="a-class b-class c-class">xxx</div>

覆蓋

<div class="a-class b-class" [class]='classTxt'>xxx</div>

classTxt = 'c-class'時,將會覆蓋原有class。實際輸出:

<div class="c-class">xxx</div>

這種作法,彷佛很雞肋,沒想出使用場景。我就是列出來,作個區分

痛點

<div class="a-class b-class" 
  [class.c-class]='cCondition'
  [class.d-class]='dCondition'
  ...
>xxx</div>

若是有N個class,須要用變量做爲條件控制,這就變的很尷尬。

這個時候就要祭出NgClass了。

5.2.NgClass

用法示例:

<!-- 字符串 -->
<div class="a-class" [ngClass]="'c-class d-class'">xxx</div>
<!-- 數組 -->
<div class="a-class" [ngClass]="['c-class','d-class']">xxx</div>
<!-- 對象:開關單個class -->
<div class="a-class" [ngClass]="{'c-class':condtionC,'d-class':condtionD}">xxx</div>
<!-- 對象:開關一組class -->
<div class="a-class" [ngClass]="{'c-class d-class':condtionCD}">xxx</div>

a-class始終存在,[ngClass]控制是否追加c-classd-class
可見NgClass,很是靈活

想不明白,爲啥還要提供[class]這種操做方式。像vue就只是提供了 :class而已。
有時候選擇多了,也未必是件好事兒。

6.style綁定

6.1.基本命令

[style.style-property(.unit)]="value"

<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>

很少說,提煉備忘

6.2.NgStyle

<div style="background: red;" [ngStyle]="{'color': 'white'}">xxx</div>
<div style="color: yellow;" [ngStyle]="{'color': hasGreenHat?'green':'red'}">xxx</div>
<div style="background: yellow;" [ngStyle]="varInComponent">xxx</div>
  • 可在原有樣式基礎上追加
  • 覆蓋原有的某個樣式
  • 可直接賦值上下文變量

7.條件渲染

7.1.NgIf

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

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

8.列表渲染

基本示例

<table width="100%">
  <tr>
    <th align="left">id</th>
    <th align="left">name</th>
    <th align="left">index</th>
    <th align="left">isFirst</th>
    <th align="left">isLast</th>
    <th align="left">odd</th>
    <th align="left">even</th>
  </tr>
  <tr *ngFor="let item of list; let i=index;let f=first;let l=last;let e=even;let o=odd; trackBy: trackById" [class.odd]="o">
    <td>{{item.id}}</td>
    <td>{{item.name}}</td>
    <td>{{i}}</td>
    <td>{{f}}</td>
    <td>{{l}}</td>
    <td>{{o}}</td>
    <td>{{e}}</td>
  </tr>
</table>
export class AppComponent {
  list = [
    {id:6,name:"name-a"},
    {id:5,name:"name-b"},
    {id:4,name:"name-c"},
    {id:3,name:"name-d"},
    {id:2,name:"name-e"},
    {id:1,name:"name-f"}
  ]  
  trackById(index: number, item: any): number { return item.id; }
}

內置變量

index(number) 引索
first(boolean) 是否第一行
last(boolean) 是否第二行
even(boolean) 引索是不是偶數,不是指是否偶數行
odd(boolean) 引索是不是奇數,不是指是否奇數行

以上內置變量,必需要在ngFor顯示聲明,不然默認是拿不到的

ngFor的內容並非模板表達式,而是Angular中的微語法

let item of list; index as i;first as f;last as l;even as e;odd as o;
let item of list; let i=index;let f=first;let l=last;let e=even;let o=odd;

上面,2個寫法是等價的,1更短,2更直觀,推薦2

trackBy

須要指定一個function,用於提高性能,提升元素的複用性,避免將全部列表單元重繪。

vue中的track-by:key同一個做用

9.事件處理

安利一篇文章,感受總結的不錯

《Angular 4.x Events Bubbling》

探索至此,發現有一個梳理得很是棒的的博客,強烈安利: Angular 4.x 修仙之路
相關文章
相關標籤/搜索