前集回顧html
在上一章裏咱們講了如何爲angular2
搭建開發環境(還沒搭起來的趕忙去看哦),並使之跑起來咱們的第一個"My First Angular 2 App"。固然也有很多朋友反映環境搭建彷佛比較複雜,整整一篇教程,最後只簡單輸出了一句話!這裏我要說一句,學習新知識的確有一個陣痛的過程,尤爲像angular2
這種框架,引入了大量之前"前端"並不關心(沒有需求)的技術棧,這使得對於以前沒有接觸過這些概念的朋友的學習曲線陡然飆升,相信很多人看了上一章裏開篇時的那些名詞後已經認識到這一點了!本教程主打實際操做,但也不會徹底忽略理論,咱們邊作邊理解。今天就接着上一章的餘溫,咱們來寫一個簡單component
。前端
本章源碼:componentwebpack
本章使用angular2
版本爲:2.4.5
, webpack
版本爲: 2.2.0
git
先來看看咱們將要完成的效果圖:angularjs
(注意動畫的部分)很是簡單的一個component
,有木有?那好,咱們如今要作的就是爲這樣一個component
描述需求:es6
她要能接受一個object
用來描述初始值,如:isChecked
(是否選中)、 txt
(顯示文本)github
當選中時,須要有橫線覆蓋文本;反之亦然web
當用戶點擊複選框時,須要向上廣播該事件,由父組件(調用方)決定點擊時該作什麼。這裏咱們須要在父組件裏改變component
的isChecked
狀態,並使component
重繪typescript
她必須是一個處理Unidirectional Data Flow(單向數據流)的component
,意思是傳入參數必須不可變(Immutable)shell
注:第4步裏,咱們使用
Unidirectional Data Flow
模型來更新數據,並無涉及到任何Reactive Programming的知識點
爲了完成以上需求,咱們須要瞭解下面知識點
或者這麼問,AngularJS裏有directive;angular2裏有component
,他們是什麼關係?該如何理解angular2裏的component
?原諒我這裏就再也不詳述AngularJS裏的directive了,直接介紹component
:
Component
: 簡單說,就是帶template
的directive
,也是最多見的組件形式。譬如:上一章中,ts/app.ts
裏的AppComponent
。
Structural directive
: 經過增長/刪除DOM
元素改變DOM
佈局的directive
。譬如:NgFor和NgIf
Attribute directive
: 控制DOM
元素顯示/隱藏,或者改變元素行爲的directive
。譬如:NgStyle
看過我以前介紹以BDD手寫依賴注入(dependency injection)的朋友應該已經對"行爲驅動"多少有些瞭解了。當咱們須要設計一個API或者組件時,最佳的方式就是先設計她的使用場景,從行爲開始,對該API或者組件進行描述,最後再將缺失的「實現」部分補全就能夠了。
假設咱們將在上一章中的AppComponent
裏使用這個新的component
,根據以前的需求描述,咱們的使用場景應該是這個樣子的 :
import {Component, OnInit} from '@angular/core'; import {Item} from './CheckableItem'; //該component使用checkable-item做爲selector //並能夠經過[item]屬性傳入一個object //還能夠經過(onItemClicked)接受一個點擊事件 @Component({ selector: 'my-app', template: ` <h1>My First Angular 2 App</h1> <checkable-item [item]="itemInfo" (onItemClicked)="toggle($event)"> </checkable-item> ` }) export class AppComponent implements OnInit { itemInfo: Item; //當實現OnInit接口時,必須重寫ngOnInit方法 //關於OnInit,詳見: //https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-overview ngOnInit() { //設定初始值 //根據需求第1條,包含兩個屬性 this.itemInfo = { isChecked: false, txt: 'Hello World!' }; } //根據需求第3條,點擊component後,事件要 //冒泡到父組件(調用方) toggle(item: Item) { //當獲取到CheckableItem的點擊事件時, //給itemInfo從新賦值,並將isChecked置反 //注:從新賦值是根據需求第4條的不可變性 this.itemInfo = { isChecked: !item.isChecked, txt: item.txt }; } }
component
根據上述介紹,再結合以前的效果圖,咱們要作的固然就是一個標準的Component
。她有template
,而且包含了至少一個input
和一個label
標籤。
有了使用場景(行爲),接下來就是實現這個CheckableItem
了:
touch ts/CheckableItem.ts
向剛建立的ts/CheckableItem.ts
文件裏寫入以下內容:
import {Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core'; @Component({ //髒檢查策略,OnPush指當且僅當傳入參數的reference發生變動時 //觸發組件重繪。這和React中的shouldComponentUpdate殊途同歸, //不過更先進(由於React仍是須要手動實現的) //這也是上一步裏itemInfo必須從新賦值的緣由 changeDetection: ChangeDetectionStrategy.OnPush, selector: 'checkable-item', //僅在當前component做用域下有效的class styles: [` .deleted{ text-decoration: line-through; } `], //template就如咱們需求裏的描述那樣,由一個input標籤和 //一個label標籤組成 template: ` <div> <input type="checkbox" (change)="clickItem($event)"> <label [class.deleted]="item.isChecked">{{ item.txt }}</label> </div> ` }) export class CheckableItem { //item被聲明爲Input,即會在父組件傳入參數時用到 @Input() item: Item; //onItemClicked被聲明爲Output,用來在用戶點擊input標籤 //時向上冒泡事件 @Output() onItemClicked = new EventEmitter(); //監聽input上的click事件,當用戶點擊時,首先阻止默認行爲 //由於是否變化(重繪)是由父組件決定的 //而後冒泡點擊事件 clickItem(e: MouseEvent) { e.preventDefault(); this.onItemClicked.emit(this.item); } } export interface ToggleItemHandler { (item: Item): void; } export interface Item { isChecked?: boolean; txt?: string; }
有朋友看到這裏,對[]
、 ()
之類的綁定標籤表示不解,這裏咱們統一來解釋:
[target] = "expression"
,將右邊表達式對應的值綁定到左邊的target
。譬如:在ts/app.ts
裏,咱們使用[item]="itemInfo"
將itemInfo
對應的值綁定到了組件CheckableItem
的item
上,這樣,在CheckableItem
裏就能夠經過this.item
獲取到父組件傳進來的參數了。
(target) = "statement"
,將左邊的事件傳遞給了右邊的表達式(一般就是事件處理函數)。譬如:在ts/app.ts
裏,咱們使用(onItemClicked)="toggle($event)"
將CheckableItem
冒泡上來的onItemClicked
事件傳遞給了toggle
函數。
[class.deleted]="item.isChecked"
,是class
的一種特殊用法,指當item.isChecked
表達式爲真時,爲該標籤的class
裏增長deleted
;反之,則刪除該標籤class
裏的deleted
打開以前寫的index.ts
,增長CheckableItem
引入:
import 'core-js/es6'; import 'core-js/es7/reflect'; import 'zone.js/dist/zone'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; //引入CheckableItem import {CheckableItem} from './CheckableItem'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, CheckableItem ],//引入聲明 bootstrap: [ AppComponent ] }) class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
OK,事已至此,咱們是否是又該啓動一把程序看看效果了?
npm start
你又看到了偉大的效果:
下回預告:小刀升級 - 多component
協做