來源:http://kittencup.com/javascript/2015/07/20/Angular%202%E6%A8%A1%E6%9D%BF%E8%AF%AD%E6%B3%95.html
原文地址:http://victorsavkin.com/post/119943127151/angular-2-template-syntax
屬性和事件綁定
屬性和事件綁定在指令中是公開的API,數據從屬性綁定流入指令,從事件中流出指令
屬性綁定
假設我們有一個組件來呈現的Todo,我們可以在我們的模板中使用這個組件,如下所示:
<todo-cmp [model]="myTodo"></todo-cmp>
這告訴Angular,只要myTodo發生變化,當有一個新的todo時,Angular會通過模型來自動更新todo組件
事件綁定
現在,讓我們添加一個事件使組件更有趣。
<todo-cmp [model]="todo" (complete)="onCompletingTodo(todo)"></todo-cmp>
當事件complete觸發時會告訴Angular調用onCompletingTodo方法
現在,讓我們看看TodoCmp本身。
@Component({ selector: 'todo-cmp', properties: ['model'], events: ['complete'] }) class TodoCmp { model; complete = new EventEmitter(); // TypeScript 支持初始化 onCompletedButton() { this.complete.next(); // 觸發事件 } }
這個組件聲明數據綁定的名字為model,事件名為complete
Angular使用Rx事件風格來處理事件的接收,EventEmitter即實現了observable,又實現了observer接口(規范),因此,我們可以用它來觸發事件,並且Angular也可以用它來監聽事件。
正如你所看到的,屬性和事件綁定的核心語法很簡單。在核心語法之上,Angular提供了一些語法糖,使表達常用的編程模式更加容易。重要的是要了解,這僅僅是語法糖,而且它不會改變語義。
雙向綁定
雙向數據綁定在某些情況下是很方便的,最值得注意的是處理輸入,正如我剛才提到的,屬性綁定用於數據從父傳遞給子,事件綁定用於從子到父的數據傳遞。因此,我們可以使用兩種方法來實現雙向綁定。
<input [ng-model]="todo.text" (ng-model)="todo.text=$event"></input>
雖然這可以正常工作,但這太羅嗦。因為這是一個常見的模式,所以Angular提供語法糖簡化這種寫法。
<input [(ng-model)]="todo.text"></input>
要完成這個例子,讓我們來實現Angular 2的ng-model
@Directive({
selector: '[ng-model]',
properties: ['ngModel'],
events: ['ngModelChanged: ngModel'],
host: {
"[value]": 'ngModel',
"(input)": "ngModelChanged.next($event.target.value)"
}
})
class NgModelDirective {
ngModel:any; // stored value
ngModelChanged:EventEmitter; // an event emitter
}
這是一種比較幼稚的ngModel實現,但它說明了如何實現雙向數據綁定行為:輸入將在text發生更改時更新,並在輸入更改時更新text字段。
請注意,於Angular 1相反,你只要看看模板就可以知道哪些綁定是“雙向”,哪些是“單向”。
此外,因為只有一個方向,屬性綁定,被Angular自動執行,雙向的行為更可預測的。
它不會破壞任何Angular 2的語法:這只是一些語法糖,沒有別的。
我想提一提,你不用去實現NgModel。Angular 2配備了一個表單處理模塊,其包括NgModel的實現。
插值
<div>Hello {{name}}</div>
使用語法糖
<div [textContent]="interpolate(['Hello'], [name])"></div>
綁定直接量
<show-title title="Some Title"></show-title>
使用語法糖
<show-title [title]=" 'Some Title' "></show-title>
移除括號
你可以使用bind-,in-和bindon-在你的模板中代替所有的括號,雖然這幾乎不能稱為“語法糖”
<some-component [prop]="someExp" (event)="someEvent()" [(two-way-prop)]="someExp"></show-title>
於上面相同
<some-component bind-prop="someExp" on-event="someEvent()" bindon-two-way-prop="someExp"></show-title>
局部變量
有兩個組件互相交互,這種情況並不少見,Angular 2在模板中支持定義的局部變量。
<video-player #player></video-player> <button (click)="player.pause()">Pause</button>
#player表示當前video-player組件自身,而沒有#player的組件則可以通過player訪問video-player組件
<input #i> {{i.value}}
語法糖
<video-player #player></video-player>
相當於
<video-player var-player></video-player>
模板和 *
Angular以特殊的方式對待template元素。它們用來創建視圖,你可以動態操作DOM塊,*語法是一種捷徑,它可以使你不用寫出全部的<template>
元素,讓我告訴你它是如何工作。
假設我們呈現Todo列表組件。
<todo-cmp *ng-for="#t of todos; #i=index" [model]="t" [index]="t"></todo-cmp>
去除語法糖后變為
<todo-cmp template="ng-for #item of items; #i=index" [model]="t" [index]="i"></todo-cmp>
再一次去除語法糖后變為
<template ng-for #item="$implicit" [ng-for-of]="items" #i="index"> <todo-cmp [model]="t" [index]="t"></todo-cmp> </template>
該ngFor指令在視圖上會創建$implicit和$index變量並綁定到模板上,在模板元素上聲明的所有變量只能在元素內提供,這就是為什么以下是不正確
<todo-cmp *ng-for="#t of todos"></todo-cmp> {{t}} <!-- t cannot be referenced here -->
你需要重點理解的是,當你建立你自己的指令來處理視圖時,你的 * syntax 語法能擴展成什么。例如,如果你看ngfor,你會發現它有ngforof屬性,但是沒有of屬性。
為什么不對ng-for自定義語法,象angular 1一樣呢?這有幾個很好的理由。有自定義語法意味着你必須知道這個微語言如何操作。這也意味着在工具(例如,IDE和LInter)中不能理解你的模板而且並不能提供自動完成和重構。
web組件和原生元素
在這篇博客我談到了一切的內容 - 局部變量,屬性和事件綁定 - 對於web組件和標准的html元素來講使用方式是完全相同
我可以用Web組件更換TodoCmp,仍然使用相同方式與它進行交互。
<todo-cmp [model]="todo" (completed)="onCompletingTodo(todo)"></todo-cmp>
我可以用Web組件更換視頻播放器,仍然使用相同方式與它進行交互。
<video-player #player></video-player> <button (click)="player.pause()">Pause</button>
這將變得極為重要,因為更多的組件庫可用。當運行在非Angular組件上也很重要(這是Angular 2目標之一),否者你不得不把每一個原生組件包裝成一個Angular 2組件,那真的很糟糕。Angular 2你可以直接使用任何本地組件,使用相同的語法,只有當你例如想使用依賴注入時,可將它包裝為Angular組件
設計目標
我希望你能明白在Angular 2里模板語法是怎么運作的.現在,我想來討論一下為什么它用這種方式運作?它這么設計的目的是什么?
我們的主要設計目標是使模板更加清晰,開發者在不知如果使用指令情況也能夠理解和重構模板,以及指令是如何工作的。
<component [property1]="name" property2="name"></component>
不管組件元素是什么,property1是指向一個name變量的屬性 property2屬性只表示一個name字符串,你也需要知道,組件的property2屬性無法更新,property1屬性綁定更新是從父到子。
<component [(property1)]="name"></component>
在這里,我們可以看到,name可以被更新,因為我們使用[()]語法
同樣,你可以告訴在模板中定義了哪些變量,來看這里的*nf-for
<todo-cmp *ng-for="#t of todos"></todo-cmp>
其次,我們希望有豐富的開發工具,在下面的列子中,工具可以靜態地推斷出第一個index是組件上的字段,而第2個index是ng-for輸出的局部變量
{{index}}
<div *ng-for="#item of items; var index=index"> {{index} </div>
這就是其中的一個Angular 2模板的實例分析。由於IDE可以分析模板,所以他們可以提供自動完成和重構。
最后,我們要本地組件和web組件無縫集成。這意味着Angular 不能有特殊的特定事件(例如,ng-click),而必須提供通用機制,更新任何屬性或監聽任何事件。