Angular 2 Series: Components
By Max on April 13, 2015
歡迎來到我們 Angular 2 的第二章。如果你沒看過第一章,點這(介紹Angular2)看看
在這章中,我們將介紹一下組件,這玩意優雅的取代掉了 Angular 1 中的 controller, scope, 和 directive。
也許你沒看過 Igor 和 Tobias 十月份的時候在 ngEurope 上做的精彩演說,但是我當時在場,他們繪聲繪色的宣佈了 Angular 1 的各種概念死亡,包括了 controller 和 scope
,當一排讓人不悅的墓碑 duang~ 到大屏幕上的時候,現場人羣裏面緊張的呼吸和莫名的笑聲讓我記憶猶新。
之後在社區引起了軒然大波,有 scathing comments, high-profile departures,也有 overly-dramatic banter 讓人足夠興奮。
雖然譁眾取寵確實能賺點擊率,不過過剩反應確實是沒必要。那我們來看看新的 Angular 2 是怎樣用它的組件模型,以更簡單的方式來實現 v1 中的一些功能。
Angular 1 並不是圍繞組件這個概念來站開的。所以,我們需要以 controller 模式,把我們的邏輯依附(attach)到頁面的各個地方。Scope 就被帶到各處,添加或者傳遞,根據你是怎樣封裝你的自定義指令。(isolate scope,有沒有!)。
我真的不是很清楚應該怎麼描述 v1 的模型,不過它真的和我用過的其他系統不一樣。我只能說"依附"各種行為到頁面的各個部分,然後再必須處理這些複雜的我們通過 directive , controller 和 scope 創建的指令元結構(meta-structure)。
為了把邏輯添加到頁面上,我們就得在創建一個 controller 還是 directive 之間做個取捨。
在版本 2 , Angular 把所有這些東西都幹丟到了一個更干淨,更面向對象的組件模型中。
如果你曾經寫過 Java, C#,或者其他強 OO 類語言,你立刻就會明白什麼是組件了。它就是一個類,代表了屏幕上的一個元素,以及一些影響它外觀和行為的成員數據。
就像在 Java(Swing) 裏面的 JComponent
子類,或者在 Windows Forms 裏面的 Control
子類。
讓我們來看看例子:
<!-- lang: js -->
import {Component, Template} from 'angular2/angular2'
@Component({
selector: 'my-component'
})
@View({
inline: "<div>Hello my name is {{name}}</div>"
})
export class MyComponent {
constructor() {
this.name = 'Max'
}
sayMyName() {
console.log('My name is', this.name)
}
}
這就創建了一個叫 MyComponent
的新組件,它會定義一個叫做 <my-component>
的標籤,就像在 v1 裏面的 restrict: 'E'
。
我們可以用 @View
註解(annotation)來指定組件的模板(註解是 AtScript 和 TypeScript引進的 es6 擴展,不過只有 TypeScript 跟進了) 。
讓我們再仔細看看上面的例子,我們沒有看到 $scope
。相應的,我們看到了 this.name = 'Max'
,和一個叫做 sayMyName()
的成員方法(“method”)。如果你熟悉 v1 的 Controller As
標籤的話,那你應該也會覺得 this
很熟悉。
不過,它還是有點不一樣。當我們的組件被 “實例化(instantiated)” 和被頁面渲染的時候,我們拿到組件的一個實例。我們可以修改組件實例中的數據,調用它的方法,甚至把它當參數傳給別的組件。就是對象嘛!
再舉個例子,假設我們有一個 Button
控件。控件有一個 this.title
提供按鈕的文字,有一個點擊事件。原來,我們需要為這個按鈕創建一個新的 directive
,或者偷懶一點給它(或它的父層)寫一個 controller。在 Angular 2 組件中,我們有了一個更加自然的把這些概念放到一起的方式:我們的組件有封裝起來的數據實例(非常像 isolate scope),事件句柄(非常像在 scope 裏面的方法),還有模板(非常像 directive)。
我們甚至可以從另外的組件繼承它們,或者通過屬性給附加進來,甚至可以訪問父節點以及兄弟節點(暫且按下不提)。這遠比在 v1 裏面用 require
簡單多了。
在上一篇帖子裏,我們簡單提到過引導,這是 Angular 2 應用自我啟動的地方。在 v1,我們必須 ng-app
一下或者人工啟動。而 v2,這裏就沒有 ng-app
了。取而代之,我們需要提供根組件,放在我們希望啟動應用的地方:
<!-- lang: js -->
<app></app>
然後是 v2 的組件:
<!-- lang: js -->
@Component({ selector: 'app' })
@View({
url: 'main.html',
})
class MyApp {
constructor() {
console.log('App Start')
}
}
bootstrap(MyApp)
這就像我們在 Angular 1 中,經常可以看到的 ng-app
和 ng-controller
用在同一個元素上,比如:
<!-- lang: js -->
<body ng-app="myspace" ng-controller="AppCtrl">
然後,在我們的 AppCtrl
會創建根 scope 數據,我們的子 controller 可以訪問,或者擴展它。
在 v2,我們把這些都放到了一個組件。
我真的超喜歡這個改變。你想想你的文檔結構,是不是很像一棵樹。根節點是不是應該和別的節點沒什麽不同,即使在 v1。
隨著組件模式的改變,我們的習慣也需要有些改變。我們如果想要把邏輯放到頁面上,現在不是用 controller 了,我們需要給它創建一個新的組件。如果我們想用既存組件,但是又想要追加不同行為,我們需要擴展它。
讓我們再深入一點上面提到的 @Component
和 @View
註解。你要記住,這是一個 ES6 的擴展,被 TypeScript 用到了 Angular 2 裏面,是它的一部分。
基本上,這些註解就是給我們的組件提供附加情報用的。我們可以配置選擇器,讓它查找自己的實例(比如 <my-component>
),以及設置模板。
@Component
是核心註解,有一些很重要的參數:
<!-- lang: js -->
@Component({
selector: 'my-component',
services: [MyService]
})
selector
參數是用來取代在 v1 裏面 directive 的自動命名系統的,現在被改成需要明確指定了:你必須明確指定這個新的組件作為標記的時候的名稱。工作原理和 querySelector
很像。
services
參數是新的依賴注入系統的一部分,它指定了我們想要把哪些服務注入組件(下一篇詳述)。
@View
配置組件的模板,還有那些我們想要用到的子指令:
<!-- lang: js -->
@View({
// Url based
url: 'main.html',
// Inline based
inline: `
<div>
<button></button>
<content></content>
</div>
`,
directives: [Button]
})
在這個例子裏面,我們看到 url
參數,可以讓我們指定一個外部模板,而 inline
則可以讓組件支持內置模板。請注意這裏: new backtick Template strings ,ES6 的新玩意,能讓我們很簡單的就完成多行模板!
我們還要引入我們希望在組件裏面用到的子指令,這樣它就可以順利的編譯和實例化了(我希望在之後這裏能做改善,你不需要明確的指定引入了哪些子指令)。
最後,我們再看看在我們模板中的 <content></content>
標記。這是新的 ngTransclude
,指定了子視圖會被放到哪裡。你可以有多個子視圖,然後指定多個不同的地方來放它們(之後再說)。
Angular 2 確實發生了許多變化,很戲劇性的我們需要在頁面上創建和指定組件。
雖然從我們喜歡的 scope,controller 和 directive(那些我們超用心去學習和深深愛上的概念)中遷移過來是很痛苦的,但是它真的很值得。由於新的組件模型, Angular 已經變成了我們熟悉的 OO ,並且讓它和之後的 web 標準很接近了,比如說 Web 組件和虛擬 DOM。
一旦你習慣了新的模型,就很難再回去了。我想社區一定會去學會擁抱改變,並很樂於看到它們讓 Angular 2 變得更好。
在下一篇帖子裏,我們將聊一下數據綁定“新方式”:英年早逝的雙向數據綁定!
敬請期待。