如今的前端開發,都朝元件式開發模式靠攏,如果使用目前最流行的前端架構Angular和React開發應用,不可避免地需要開發元件,也就意味着我們需要考慮元件間的資料傳遞等問題,不過Angular 2已經為我們提供了很好的解決方案。
父元件和子元件
接觸過面向對象程式設計的開發者肯定不會對父子關系陌生,在Angular 2中子元件存在于父元件“體内”,并且父子元件可以通過一些管道進行通訊。
父元件向子元件傳入資料 – @Input
當我們着手開始開發一個元件時,第一件想到的應該就是為其傳入資料,畢竟我們期望元件為我們處理某些工作通常就需要給其提供“養料”,畢竟不能又讓馬兒跑,又不給馬兒吃草。Angular 2中子元件使用裝飾器
@Input
接收父元件傳入的資料:
// child-component.ts
import { OnInit, Component, Input } from '@angular/core';
@Component({
selector: 'child-component',
...
})
export class ChildComponent implements OnInit {
@Input
count: number = ;
ngOnInit() {
console.log(this.count); // 父元件内傳入的值或者我們自己設定的初始值0
}
increaseNumber() {
this.count ++;
}
descreaseNumber() {
this.count --;
}
}
可以看到,我們使用裝飾器
@Input
修飾了count屬性,這就意味着
child-component
被使用時期望收到一個名為count的屬性,當然不屬于自己掌控的範圍内要小心行事,别人使用我們的元件時什麼情況都可能出現,是以我們為count設定了一個初始值,當父元件沒有給我們的count屬性傳值時,我們就取此初始值。
// father-component.ts
import { Component } from '@angular/core';
import { ChildComponent } from '../child-component/child-component';
@Component({
template: `
<child-component [count]='initialCount'></child-component>
`,
...
})
export class FatherComponent {
initialCount: number = 5;
}
父元件使用
child-component
時,為count屬性賦予初始值
initialCount
,即5,也就是說此時
ChildComponent
的
ngOnInit
方法中會列印出5。注意
[count]
文法辨別了資料流向:父元件流入子元件,即單向資料綁定。此時如果傳入的資料是基本資料類型,子元件中對數組做任何操作都不會影響到父元件,但如果傳入的不是基本資料類型,而是引用資料類型,則要格外注意子元件中對資料的操作可能會對父元件産生影響。
子元件通知父元件資料已處理完成 – @Output、EventEmitter
父元件傳入資料給子元件之後并不是萬事大吉了,就像父母養育孩子,供其讀書,但孩子需要把學習進度、考試成績等呈現給父母看(不管是否自願…),父元件也需要子元件在合适的時機通知自己資料已經處理好,可以檢閱了。而此時就需要使用@Output和EventEmitter了。
// father-component.ts
import { Component } from '@angular/core';
import { ChildComponent } from '../child-component/child-component';
@Component({
template: `
<child-component [count]='initialCount' (change)="countChange($event)"></child-component>
`,
...
})
export class FatherComponent {
initialCount: number = 5;
countChange($event) {
}
}
看看我們在父元件中加入了什麼東東:
1.
(change)
,看到這樣的文法第一時間就知道這是事件綁定,也就是說我們在父元件中監聽子元件的某些變化,并能夠在其變化時作出相關操作;
2.增加了
countChange
方法作為
change
事件的處理函數。
但是稍等,當我們為input标簽指定type、placeholder等屬性時,我們知道它們都已經被“實作了”,所謂“實作”,即這些屬性在input标簽上是有意義的。但是目前這裡我們為
child-component
指定了名為
change
的事件是沒意義的,因為其并未“實作”
change
事件,于是下一步我們就需要使用@Output和EventEmitter将其變得有意義:
// child-component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'child-component',
...
})
export class ChildComponent {
@Input
count: number = ;
@Output
change = new EventEmitter();
increaseNumber() {
this.count ++;
this.change.emit(this.count);
}
descreaseNumber() {
this.count --;
this.change.emit(this.count);
}
}
讓我們再來看看在子元件中增加了什麼東東:
1.使用裝飾器@Output修飾了
change
屬性,并為其賦了初值為EventEmitter的執行個體;
2.在
increaseNumber
和
descreaseNumber
方法修改了
count
屬性後,調用了
change
屬性的
emit
方法通知父元件。
此時,我們在
ChildComponent
中實作了
change
,于是父元件中為
child-component
綁定
change
事件也就有意義了:當子元件通知父元件時,父元件可以擷取到通知中攜帶的資料并進行下一步操作:
// father-component.ts
...
countChange($event) {
this.initialCount = $event;
}
...
總結
不知道你有沒有發現,其實上面我們模拟了“雙向資料綁定”:父元件将資料傳入子元件,子元件改變資料時通知父元件進行“同步更新”。但是要注意其實資料流向是單向的,即資料是父元件單向流入子元件,父元件資料的更新是通過子元件的事件通知以後才被更新。也就是說其實在Angular 2中:雙向資料綁定 = 單向資料綁定 + 事件,以我們最熟悉的
ngModel
為例:
和下面的寫法是等價的:
同樣的,如果将我們的
child-component
元件寫作雙向資料綁定的形式即為:
參考文檔
了解Ionic 2之class及其修飾器@App、@Pipe
@Input官方文檔
@Output官方文檔
EventEmitter官方文檔
元件間通信