angular2初入眼帘之-多components协作
前集回顧
在上一章里我們講了如何在angular2下開發(fā)一個(gè)component(還沒做的趕緊去學(xué)吧)。我們使用了Unidirectional Data Flow模式書寫component,并引入了Immutable思想,這些以前只在React里見到的設(shè)計(jì),現(xiàn)在angular2里也有體現(xiàn),并且在本章中會(huì)著重講解多components的協(xié)作。
本章源碼:multicomponents
本章使用angular2版本為:2.4.5,webpack版本為: 2.2.0
先來看看我們將要完成的效果圖:
需求分析
(注意動(dòng)畫部分),由上一章的一個(gè)component,變成了一個(gè)輸入component、 一個(gè)遍歷顯示component、 一個(gè)總結(jié)component。畫一個(gè)組件樹的示意圖如下:
圖片描述
分析第一部分
我們將其命名為InputItem
它由一個(gè)input[type="text"]和一個(gè)button組成
當(dāng)點(diǎn)擊button時(shí),需要向上冒泡事件,并組合一個(gè)新的CheckableItem隨事件發(fā)送出去
清空input[type="text"]
第3步操作,也可以通過鍵盤敲擊"回車鍵"完成操作
分析第二個(gè)遍歷顯示部分
參考上一章
關(guān)于*ngFor
分析第三個(gè)總結(jié)部分
我們將其命名為Counter
它由一個(gè)span組成,顯示總結(jié)信息
它接受一個(gè)items參數(shù),用來生成總結(jié)信息
總結(jié)信息為:顯示當(dāng)前還有多少個(gè)isChecked === false的item
設(shè)計(jì)use case
還是老套路,先來設(shè)計(jì)這些新的components的使用場景(這種方式,我們稱之為"BDD",不了解的朋友參考以BDD手寫依賴注入。
重構(gòu)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[] = [];//當(dāng)捕獲到onItemAdded事件時(shí),調(diào)用該方法,添加新item到items里//注:根據(jù)Immutable策略,生成新的itemsaddItem(item: Item) {this.items = [...this.items, item];}//點(diǎn)擊checkable-item時(shí),置反其isChecked屬性//注:根據(jù)Immutable策略,生成新的itemstoggle(item: Item, index: number) {this.items = [...this.items.slice(0, index),{ isChecked: !item.isChecked, txt: item.txt },...this.items.slice(index + 1)];} }實(shí)現(xiàn)InputItem
touch ts/InputItem.ts向剛創(chuàng)建的ts/InputItem.ts中,添加如下內(nèi)容:
import {Component, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core';@Component({//這里仍然使用OnPush策略changeDetection: ChangeDetectionStrategy.OnPush,selector: 'input-item',//template里包含一個(gè)input[type="text"]和button//外面又一個(gè)form標(biāo)簽是因?yàn)樾枨笾邢M剀囨I也可以觸發(fā)操作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();//無論點(diǎn)擊button、還是敲擊回車鍵,都處罰添加事件//組裝一個(gè)新的item對象,//清空textonSubmit() {this.onItemAdded.emit({isChecked: false,txt: this.text});this.text = '';} }實(shí)現(xiàn)Counter
touch ts/Counter.ts向剛創(chuàng)建的ts/Counter.ts中,添加如下內(nèi)容:
import {Component, OnChanges, SimpleChange, Input, ChangeDetectionStrategy} from '@angular/core';import {Item} from './CheckableItem';@Component({//這里仍然使用OnPush策略changeDetection: ChangeDetectionStrategy.OnPush,selector: 'counter',//template包含一個(gè)spantemplate: `<span>We have {{ length }} item{{ postFix }}</span>` }) export class Counter implements OnChanges {//接受items參數(shù)@Input() items: Item[];postFix: string;length: number;//每次當(dāng)參數(shù)items的reference發(fā)生變化時(shí),觸發(fā)該方法//獲取新的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,所以數(shù)據(jù)的變更都是Immutable的。如果之前寫過React,那對于這種書寫方式一定無比熟悉。每次數(shù)據(jù)的變更,無論是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,代碼寫到這里基本就結(jié)束了,看看效果吧
npm start你又看到了偉大的效果:
下回預(yù)告:使用service
總結(jié)
以上是生活随笔為你收集整理的angular2初入眼帘之-多components协作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。