前集回顧html
在上一章裏咱們講了如何在angular2
下開發一個component
(還沒作的趕忙去學吧)。咱們使用了Unidirectional Data Flow模式書寫component
,並引入了Immutable思想,這些之前只在React裏見到的設計,如今angular2
裏也有體現,而且在本章中會着重講解多components
的協做。react
本章源碼:multicomponentswebpack
本章使用angular2
版本爲:2.4.5
,webpack
版本爲: 2.2.0
git
先來看看咱們將要完成的效果圖:es6
(注意動畫部分),由上一章的一個component
,變成了一個輸入component
、 一個遍歷顯示component
、 一個總結component
。畫一個組件樹的示意圖以下:github
圖片描述web
咱們將其命名爲InputItem
typescript
它由一個input[type="text"]
和一個button
組成shell
當點擊button
時,須要向上冒泡事件,並組合一個新的CheckableItem
隨事件發送出去npm
清空input[type="text"]
第3步操做,也能夠經過鍵盤敲擊"回車鍵"完成操做
咱們將其命名爲Counter
它由一個span
組成,顯示總結信息
它接受一個items
參數,用來生成總結信息
總結信息爲:顯示當前還有多少個isChecked === false
的item
仍是老套路,先來設計這些新的components
的使用場景(這種方式,咱們稱之爲"BDD",不瞭解的朋友參考以BDD手寫依賴注入。
ts/app.ts
import {Component} from '@angular/core'; import {Item} from './CheckableItem'; @Component({ selector: 'my-app', template: ` <h1>My First Angular 2 App</h1> <!-- 在template裏,增長input-item和counter的使用 input-item裏,捕獲onItemAdded事件,傳遞給addItem方法 --> <input-item (onItemAdded)="addItem($event)"></input-item> <!-- 使用*ngFor遍歷items變量。詳情: https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngFor --> <checkable-item *ngFor="let itemInfo of items; let i = index" [item]="itemInfo" (onItemClicked)="toggle($event, i)"> </checkable-item> <!-- counter裏,傳入items --> <counter [items]="items"></counter> ` }) export class AppComponent { //聲明items爲成員變量 items: Item[] = []; //當捕獲到onItemAdded事件時,調用該方法,添加新item到items裏 //注:根據Immutable策略,生成新的items addItem(item: Item) { this.items = [...this.items, item]; } //點擊checkable-item時,置反其isChecked屬性 //注:根據Immutable策略,生成新的items toggle(item: Item, index: number) { this.items = [ ...this.items.slice(0, index), { isChecked: !item.isChecked, txt: item.txt }, ...this.items.slice(index + 1) ]; } }
InputItem
touch ts/InputItem.ts
向剛建立的ts/InputItem.ts
中,添加以下內容:
import {Component, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core'; @Component({ //這裏仍然使用OnPush策略 changeDetection: ChangeDetectionStrategy.OnPush, selector: 'input-item', //template裏包含一個input[type="text"]和button //外面又一個form標籤是由於需求中但願回車鍵也能夠觸發操做 template: ` <form (ngSubmit)="onSubmit()"> <input type="text" [(ngModel)]="text" name="todo"> <button type="submit">Add Item</button> </form> ` }) export class InputItem { //雙向綁定到input[type="text"] text: string; //向外部冒泡的事件 @Output() onItemAdded = new EventEmitter(); //不管點擊button、仍是敲擊回車鍵,都處罰添加事件 //組裝一個新的item對象, //清空text onSubmit() { this.onItemAdded.emit({ isChecked: false, txt: this.text }); this.text = ''; } }
Counter
touch ts/Counter.ts
向剛建立的ts/Counter.ts
中,添加以下內容:
import {Component, OnChanges, SimpleChange, Input, ChangeDetectionStrategy} from '@angular/core'; import {Item} from './CheckableItem'; @Component({ //這裏仍然使用OnPush策略 changeDetection: ChangeDetectionStrategy.OnPush, selector: 'counter', //template包含一個span template: ` <span> We have {{ length }} item{{ postFix }} </span> ` }) export class Counter implements OnChanges { //接受items參數 @Input() items: Item[]; postFix: string; length: number; //每次當參數items的reference發生變化時,觸發該方法 //獲取新的length、postFix,重繪組件 //這裏和React中的componentWillUpdate很類似 ngOnChanges(changes: { [key: string]: SimpleChange }): any { let newItems: Item[] = changes['items'].currentValue; this.length = newItems.reduce((p, item) => p + (item.isChecked ? 0 : 1), 0); this.postFix = this.length > 1 ? 's' : ''; } }
CheckableItem
import {Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'checkable-item', styles: [` .deleted{ text-decoration: line-through; } `], template: ` <div> <input type="checkbox" [checked]="item.isChecked" (change)="clickItem($event)"> <label [class.deleted]="item.isChecked">{{ item.txt }}</label> </div> ` }) export class CheckableItem { @Input() item: Item; @Output() onItemClicked = new EventEmitter(); clickItem(e: MouseEvent) { e.preventDefault(); this.onItemClicked.emit(this.item); } } export interface ToggleItemHandler { (item: Item): void; } export interface Item { isChecked?: boolean; txt?: string; }
組件樹的總體編寫思路就是Unidirectional Data Flow,因此數據的變動都是Immutable的。若是以前寫過React,那對於這種書寫方式必定無比熟悉。每次數據的變動,不管是InputItem
仍是CheckableItem
,都將變化冒泡到AppComponent
,而後由AppComponent
再向下逐級推送各組件是否重繪。
打開index.ts
,增長新模塊聲明引入
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 { FormsModule } from '@angular/forms'; import { AppComponent } from './app'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import {CheckableItem} from './CheckableItem'; import {InputItem} from './InputItem'; import {Counter} from './Counter'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, CheckableItem, InputItem, Counter ], bootstrap: [ AppComponent ] }) class AppModule { } platformBrowserDynamic().bootstrapModule(AppModule);
OK,代碼寫到這裏基本就結束了,看看效果吧
npm start
你又看到了偉大的效果:
下回預告:使用service