angular2初入眼簾之-多components協做

前集回顧html

上一章裏咱們講了如何在angular2下開發一個component(還沒作的趕忙去學吧)。咱們使用了Unidirectional Data Flow模式書寫component,並引入了Immutable思想,這些之前只在React裏見到的設計,如今angular2裏也有體現,而且在本章中會着重講解多components的協做。react

本章源碼:multicomponentswebpack

本章使用angular2版本爲:2.4.5webpack版本爲: 2.2.0git

先來看看咱們將要完成的效果圖:es6

圖片描述

需求分析

(注意動畫部分),由上一章的一個component,變成了一個輸入component、 一個遍歷顯示component、 一個總結component。畫一個組件樹的示意圖以下:github

圖片描述web

分析第一部分

  1. 咱們將其命名爲InputItemtypescript

  2. 它由一個input[type="text"]和一個button組成shell

  3. 當點擊button時,須要向上冒泡事件,並組合一個新的CheckableItem隨事件發送出去npm

  4. 清空input[type="text"]

  5. 第3步操做,也能夠經過鍵盤敲擊"回車鍵"完成操做

分析第二個遍歷顯示部分

參考上一章
關於*ngFor

分析第三個總結部分

  1. 咱們將其命名爲Counter

  2. 它由一個span組成,顯示總結信息

  3. 它接受一個items參數,用來生成總結信息

  4. 總結信息爲:顯示當前還有多少個isChecked === falseitem

設計use case

仍是老套路,先來設計這些新的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

相關文章
相關標籤/搜索