Component在Angular2的世界裡是很多變也很重要的。在一個網站裡會存在很多Component,如何讓Component與Component之間做交流,當然也是一個很重要的課題
從父元件傳資料至子元件
Component可以對外定義可以接收資料的欄位. 利用@Input decorations
1 2 3 4 5 6 7 8 9 10 11
| @Component({ selector: 'hero-child', template: ` <h3>{{hero.name}} says:</h3> <p>I, {{hero.name}}, am at your service, {{masterName}}.</p> ` }) export class HeroChildComponent { @Input() hero: Hero; @Input('master') masterName: string; }
|
從父元件使用這個子元件的方式
1
| <hero-child [hero]="hero" [master]="master"></hero-child>
|
細節說明
@Input的Interface, @Input可以在小括號內指定對外的property name.
1 2 3 4 5 6
| export interface Input {
bindingPropertyName?: string; }
|
而接續在@input() 後面的是在該Component內所使用的變數,也可以指定型別給他。當然也可以分成兩行寫
1 2
| @Input() private hero: Hero;
|
或是自訂Setter/Getter
1 2 3 4 5 6
| private _name: string = '<no name set>'; @Input() set name(name: string) { this._name = (name && name.trim()) || '<no name set>'; } get name() { return this._name; }
|
@Output
如果想要從元件內的值往外傳的時候,可以使用 @Output decoration, 但是@Ouput只限定於Event
1 2 3 4 5 6 7 8
| export class VoterComponent { @Input() name: string; @Output() onVoted = new EventEmitter<boolean>(); vote(agreed: boolean) { this.onVoted.emit(agreed); } }
|
父元件使用這個子元件的方式
1
| <my-voter (onVoted)="onVoted($event)"></my-voter>
|
1 2 3 4 5 6 7
| export class VoteTakerComponent { agreed = 0; disagreed = 0; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; } }
|
EventEmitter的emit([value])會將值讓註冊在該屬性欄位的方法知道。
ngOnChanges
當Input的值被改變時,會觸發ngOnChanges事件。更多關於 ngOnChanges
,請參閱 LifeCycle Hooks
1 2 3 4 5 6 7 8 9 10
| ngOnChanges(changes: {[propKey: string]: SimpleChange}) { let log: string[] = []; for (let propName in changes) { let changedProp = changes[propName]; let from = JSON.stringify(changedProp.previousValue); let to = JSON.stringify(changedProp.currentValue); log.push( `${propName} changed from ${from} to ${to}`); } this.changeLog.push(log.join(', ')); }
|
read more
父元件操作子元件的屬性及方法
父元件可以透過給予子元件一個RefId後,直接使用子元件內的方法與屬性
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { Component } from '@angular/core'; import { CountdownTimerComponent } from './countdown-timer.component';
@Component({ selector: 'countdown-parent-lv', template: ` <h3>Countdown to Liftoff (via local variable)</h3> <button (click)="timer.start()">Start</button> <button (click)="timer.stop()">Stop</button> <div class="seconds">{{timer.seconds}}</div> <countdown-timer #timer></countdown-timer> ` }) export class CountdownLocalVarParentComponent {}
|
或父元件可以透過@ViewChild來操作子元件的方法與屬性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { Component } from '@angular/core'; import { CountdownTimerComponent } from './countdown-timer.component';
@Component({ selector: 'countdown-parent-lv', template: ` <h3>Countdown to Liftoff (via local variable)</h3> <button (click)="start()">Start</button> <button (click)="stop()">Stop</button> <div class="seconds">{{timer.seconds}}</div> <countdown-timer #timer></countdown-timer> ` }) export class CountdownLocalVarParentComponent { @ViewChild('timer') private timer: CountdownTimerComponent; @ViewChild(CountdownTimerComponent) private timer: CountdownTimerComponent;
start(){ this.timer.start(); }
stop(){ this.timer.stop(); } }
|
透過Service的方式讓父與子元件互相溝通
這個需要使用到RxJS的Object來達成這個功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; @Injectable() export class MissionService { private missionAnnouncedSource = new Subject<string>(); private missionConfirmedSource = new Subject<string>(); missionAnnounced$ = this.missionAnnouncedSource.asObservable(); missionConfirmed$ = this.missionConfirmedSource.asObservable(); announceMission(mission: string) { this.missionAnnouncedSource.next(mission); } confirmMission(astronaut: string) { this.missionConfirmedSource.next(astronaut); } }
|
利用subscribe和執行service的方法來達成訊息交換的功能
結語
Component與Component之間的溝通方式基本上並不困難,但是很多情形是有太多Component與資料間的相依關係讓事情變得很複雜,所以如何最好Component的規劃是一個需要經驗的課題,只好不斷的從實做中整理出規則。
Reference