Angular 5.x Learn Note 標簽(空格分隔): Angular 上手 官網Heroes 例子: "Demo On Github" 。 一、模板與數據綁定 1. 顯示數據 1. 選擇自定義標簽的名稱。 2. 屬性定義內聯模板; 屬性定義外鏈模板。 3. 值的聲明和初始化。 4. 插值 ...
Angular 5.x Learn Note
標簽(空格分隔): Angular
上手
官網Heroes 例子:Demo On Github。
一、模板與數據綁定
1. 顯示數據
selector
選擇自定義標簽的名稱。template
屬性定義內聯模板;templateUrl
屬性定義外鏈模板。值的聲明和初始化。
export class AppComponent { // 變數賦值的方式初始化組件 // title = 'Tour of Heroes'; // myHero = 'Vencent'; title: string; myHero: string; Heroes = ['Windstorm', 'Vencent', 'Bombasto', 'Magneta', 'Tornado',]; // 構造函數來聲明和初始化屬性 constructor() { this.title = 'Tour of Heroes'; this.myHero = this.Heroes[1]; } }
{{ [data] }}
插值表達式 (interpolation) ,模板綁定屬性;*ngFor="let [item] of [list]"
模板迴圈,ngFor
可以為任何可迭代的 (iterable) 對象重覆渲染條目;ngIf
指令會根據一個布爾條件來顯示或移除一個元素,*ngIf="[condition]"
。具體參見下一節(模板語法)。實體類的聲明。
export class Hero { constructor( public id: number, public name: string) { } }
public id: number,
- 聲明瞭一個構造函數參數及其類型
- 聲明瞭一個同名的公共屬性
new
出該類的一個實例時,把該屬性初始化為響應的參數值
2. 模板語法
- HTML是Angular模板的語言;
<script>
元素,它被禁用(忽略)了,以阻止腳本註入攻擊的風險:安全。 插值表達式
{{...}}
// 作為文本 <!-- "The sum of 1 + 1 is 2" --> <p>The sum of 1 + 1 is {{1 + 1}}</p>
// 作為屬性值 image = img,由於此md編輯器會吞掉img標簽 <image src="{{heroImageUrl}}" style="height:30px">
- 模板表達式
JavaScript 中那些具有或可能引發副作用的表達式是被禁止的:- 賦值(
=
,+=
,-=
,...) new
運算符- 使用
;
或,
的鏈式表達式 - 自增或自減操作符(
++
和--
) - 不支持位運算
|
和&
- 具有新的模板表達式運算符,比如
|
、?.
和!
典型的表達式上下文就是這個組件實例,它是各種綁定值的來源。
表達式中的上下文變數是由模板變數、指令的上下文變數(如果有)和組件的成員疊加而成的。 如果我們要引用的變數名存在於一個以上的命名空間中,那麼,模板變數是最優先的,其次是指令的上下文變數,最後是組件的成員。 - 賦值(
模板語句
模板語句用來響應由綁定目標(如 HTML 元素、組件或指令)觸發的事件。
模板語句解析器支持基本賦值 (<button (click)="deleteHero()">Delete hero</button>
=
) 和表達式鏈 (;
和,
)。
模板語句禁止以下:new
運算符- 自增或自減操作符(
++
和--
) - 操作並賦值(
+=
,-=
,...) - 位操作符
|
和&
- 模板表達式運算符,比如
|
、?.
和!
4.1 語句上下文
典型的語句上下文就是當前組件的實例。<button (click)="onSave($event)">Save</button> <button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
模板上下文中的變數名的優先順序高於組件上下文中的變數名。在上面的
deleteHero(hero)
中,hero
是一個模板輸入變數,而不是組件中的hero
屬性。模板語句不能引用全局命名空間的任何東西。比如不能引用
window
或document
,也不能調用console.log
或Math.max
。綁定語法
數據方向 語法 綁定類型 單向
從數據源
到視圖目標{{expression}}
[target]="expression"
bind-target="expression"
插值表達式
Property
Attribute
類
樣式單向
從視圖目標
到數據源(target)="statement"
on-target="statement"
事件 雙向 [(target)]="expression"
bindon-target="expression"
雙向 HTML attribute value
指定了初始值;DOM value property
是當前值。- 模板綁定是通過
property
和事件來工作的,而不是attribute
。 - 插值表達式和屬性綁定只能設置屬性,不能設置 attribute。
- 在 Angular 的世界中,attribute 唯一的作用是用來初始化元素和指令的狀態。
如圖 5-1 所示,創建了一個
input
元素,初始設置它的屬性(Attribute)為 "Bob",在獲取它的 Attribute 和 使用ng雙向綁定值時,用戶輸入值(123)和 Attribute 的值(Bob)是不一樣的。
5.1 屬性綁定 ( [屬性名] )
單向輸入
綁定目標
<img [src]="heroImageUrl">
消除副作用
只綁定數據屬性和那些只返回值而不做其它事情的方法。返回恰當的類型
模板表達式應該返回目標屬性所需類型的值。如果目標屬性想要個數字,就返回數字。HeroDetail組件的hero屬性想要一個Hero對象,那就在屬性綁定中精確地給它一個Hero對象:<app-hero-detail [hero]="currentHero"></app-hero-detail>
別忘了方括弧
方括弧告訴 Angular 要計算模板表達式。一次性字元串初始化
- 目標屬性接受字元串值。
- 字元串是個固定值,可以直接合併到模塊中。
- 這個初始值永不改變。
下麵這個例子把HeroDetailComponent的prefix屬性初始化為固定的字元串,而不是模板表達式。
<app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail>
屬性綁定還是插值表達式?
<p><span>"{{titles}}" is the <i>interpolated</i> title.</span></p> <p>"<span [innerHTML]="titles"></span>" is the <i>property bound</i> title.</p>
傾向於插值表達式
但數據類型不是字元串時,就必須使用屬性綁定了,如圖 5-2 所示:
內容安全
不管是插值表達式還是屬性綁定,都不會允許帶有 script 標簽的 HTML 泄漏到瀏覽器中。只渲染沒有危害的內容。比如下麵的綁定:<p [innerHTML]="scripts"></p>
5.2 attribute、class 和 style 綁定
attribute 綁定
這是“綁定到目標屬性 (property)”這條規則中唯一的例外。這是唯一的能創建和設置 attribute 的綁定形式。
由attr首碼,一個點 (.) 和 attribute 的名字組成。
把[attr.colspan]綁定到一個計算值:<table border="1"> <!-- 設置 colspan=2 --> <tr><td [attr.colspan]="1 + 1">1-2</td></tr> <tr><td>5</td><td>6</td></tr> </table>
運行結果:
attribute 綁定的主要用例之一是設置 ARIA attribute(譯註:ARIA指可訪問性,用於給殘障人士訪問互聯網提供便利):<!-- 創建和設置輔助技術的 ARIA 屬性 --> <button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
CSS 類綁定
由class首碼,一個點 (.)和 CSS 類的名字組成,[class.class-name]
。<!-- 使用綁定重置/覆蓋所有類名 --> <div class="bad curly special" [class]="badCurly">Bad curly</div>
可以綁定到特定的類名。<!-- 用屬性打開/關閉“special”類 --> <div [class.special]="isSpecial">The class binding is special</div> <!-- 綁定`class.special`優先順序高於class屬性 --> <div class="special" [class.special]="!isSpecial">This one is not so special</div>
樣式綁定
設置內聯樣式。由style首碼,一個點 (.)和 CSS 樣式的屬性名組成,[style.style-property]
。<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
有些樣式綁定中的樣式帶有單位:
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button> <button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
5.3 事件綁定 ( (事件名) )
事件綁定語法由等號左側帶圓括弧的目標事件和右側引號中的模板語句組成。<button (click)="onSave()">Save</button>
$event 和事件處理語句
綁定會通過名叫$event的事件對象傳遞關於此事件的信息(包括數據值)。<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value">
執行效果:
使用 EventEmitter 實現自定義事件
指令使用 Angular EventEmitter 來觸發自定義事件。
指令創建一個EventEmitter實例,並且把它作為屬性暴露出來。
指令調用EventEmitter.emit(payload)
來觸發事件,可以傳入任何東西作為消息載荷。
父指令通過綁定到這個屬性來監聽事件,並通過$event
對象來訪問載荷。
5.4 雙向數據綁定 ( [(...)] )
[(x)]
語法結合了屬性綁定的方括弧[x]
和事件綁定的圓括弧(x)
。
sizer.component.ts
:import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-sizer', templateUrl: './sizer.component.html' }) export class SizerComponent { @Input() size: number | string; @Output() sizeChange = new EventEmitter<number>(); dec() { this.resize(-1); } inc() { this.resize(+1); } resize(delta: number) { console.log(delta, +this.size) this.size = Math.min(40, Math.max(8, +this.size + delta)); console.log(this.size) this.sizeChange.emit(this.size); } }
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>
app.component.ts
:<app-sizer [(size)]="fontSizePx"></app-sizer> <!-- 等價於下麵的寫法 --> <!-- <app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer> --> <div [style.font-size.px]="fontSizePx">Resizable Text</div>
app.component.ts
:fontSizePx: number = 12;
同時註意在
app.module.ts
引入sizer.component.ts
。
運行結果如圖 5-9 所示:
內置指令
分為屬性型指令和結構型指令。
6.1 常用內置屬性型指令
屬性型指令會監聽和修改其它HTML元素或組件的行為、元素屬性(Attribute)、DOM屬性(Property)。
它們通常會作為HTML屬性的名稱而應用在元素上。- NgClass - 添加或移除一組CSS類
- NgStyle - 添加或移除一組CSS樣式
- NgModel - 雙向綁定到HTML表單元素
NgClass
指令
可以同時添加或移除多個類。
CSS 類綁定 是添加刪除單個類的最佳途徑。
而同時添加或移除多個 CSS 類,更好的選擇可能是NgClass
。// NgClass canSave: boolean = true; isUnchanged: boolean = false; isSpecial: boolean = true; currentClasses: {}; setCurrentClasses() { this.currentClasses = { 'saveable': this.canSave, 'modified': !this.isUnchanged, 'special': this.isSpecial } } ngOnInit(): void { // 初始化 currentClasses this.setCurrentClasses(); }
把
NgClass
屬性綁定到currentClasses
,根據它來設置此元素的CSS類:<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
運行結果如圖 6-1所示:
NgStyle
指令
NgStyle
綁定可以同時設置多個內聯樣式。
同樣的,樣式綁定也是設置單一樣式值的簡單方式。
NgStyle
是同時設置多個內聯樣式更好的選擇。// ngStyle currentStyles: {}; setCurrentStyles() { this.currentStyles = { 'font-style': this.canSave ? 'italic' : 'normal', 'font-weight': !this.isUnchanged ? 'bold' : 'normal', 'font-size': this.isSpecial ? '24px' : '12px' } }
把
NgStyle
屬性綁定到currentStyles
設置元素樣式:<div [ngStyle]="currentStyles"> This div is initially by ngStyle. </div>
運行結果如圖 6-2所示:
NgModel
- 使用[(ngModel)]雙向綁定到表單元素
雙向綁定表單元素。
需要引入FormModule
。import { FormsModule } from '@angular/forms'; @NgModule({ imports: [ FormsModule ] }) export class AppModule { }
ngModel
可以通過之前的屬性綁定和事件綁定實現:<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value">
ngModel
的展開實現(自己的輸入(ngModel
)屬性和輸出(ngModelChange
)屬性):<input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">
ngModel
的合併實現方法是[(ngModel)]
:<input [(ngModel)]="currentHero.name">
[(ngModel)]
只能設置數據綁定屬性,如果有特別需求也可以展開形式:
app.component.html
:<input [ngModel]="currentHero.name" (ngModelChange)="setUppercaseName($event)">
app.component.ts
:setUppercaseName(name) { this.currentHero.name = name.toUpperCase(); }
執行效果如下圖(圖 6-3)所示:
6.2 常用內置結構型指令
結構型指令的職責是HTML佈局。添加、移除和操縱DOM元素。
註意指令前面的*
號。- NgIf - 根據條件把一個元素添加到DOM中或從DOM移除
- NgSwitch - 一組在替代視圖之間切換的指令
- NgForOf - 對列表中的每個條目重覆套用一個模板
NgIf
指令
和vue的v-if
,v-show
一樣,不同於隱藏元素。
ngIf
可以防範空指針錯誤<div *ngIf="currentHero">Hello, {{ currentHero.name }}</div> <div *ngIf="nullHero">Hello, {{ nullHero.name }}</div>
NgFor 指令
重覆器指令。可以用在簡單的元素上,也可以用在一個組件元素上。
賦值給*ngFor
的文本是用於指導重覆器如何工作的指令 - 微語法(microsyntax)。<div *ngFor="let hero of heroes; let i = index">{{ i }} - {{hero.id + ': ' + hero.name }}</div>
trackBy
可以防止渲染全部條目。
TODO 效果已經實現,counts顯示待解決。