通過輸入綁定數據從父項傳遞給子項
這是接受數據的子元件的內容hero-child.component.ts
子元件輸要被輸入的屬性有兩個,以@Input開頭來做宣告:
import { Component, Input } from '@angular/core'; import { Hero } from './hero'; @Component({ selector: 'app-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; }
而這是父元件的內容,透過屬性綁定將值塞進子元件中
<app-hero-child *ngFor="let hero of heroes" [hero]="hero" [master]="master">
父元件的完整程式碼如下:
import { Component } from '@angular/core'; import { HEROES } from './hero'; @Component({ selector: 'app-hero-parent', template: ` <h2>{{master}} controls {{heroes.length}} heroes</h2> <app-hero-child *ngFor="let hero of heroes" [hero]="hero" [master]="master"> </app-hero-child> ` }) export class HeroParentComponent { heroes = HEROES; master = 'Master'; }
最後結果呈現:
攔截屬性的更改與設置
可將子元件的input宣告改為getter和setter,如下面範例
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-name-child', template: '<h3>"{{name}}"</h3>' }) export class NameChildComponent { private _name = ''; @Input() set name(name: string) { this._name = (name && name.trim()) || '<no name set>'; } get name(): string { return this._name; } }
這樣當父元件傳空字串來時,子元件會自動輸出<no name set>字樣
Intercept input property changes with ngOnChanges()
在子組件裡,除了上述使用getter及setter外,也可以利用ngOnChanges()來取得改變的變數並做出回應
ngOnChanges()會傳入所有被改變的值changes: {[propKey: string]: SimpleChange}
下面是一個使用範例
子元件內容
import { Component, Input, OnChanges, SimpleChange } from '@angular/core'; @Component({ selector: 'app-version-child', template: ` <h3>Version {{major}}.{{minor}}</h3> <h4>Change log:</h4> <ul> <li *ngFor="let change of changeLog">{{change}}</li> </ul> ` }) export class VersionChildComponent implements OnChanges { @Input() major: number; @Input() minor: number; changeLog: string[] = []; ngOnChanges(changes: {[propKey: string]: SimpleChange}) { let log: string[] = []; for (let propName in changes) { let changedProp = changes[propName]; let to = JSON.stringify(changedProp.currentValue); if (changedProp.isFirstChange()) { log.push(`Initial value of ${propName} set to ${to}`); } else { let from = JSON.stringify(changedProp.previousValue); log.push(`${propName} changed from ${from} to ${to}`); } } this.changeLog.push(log.join(', ')); } }
父元件內容
import { Component } from '@angular/core'; @Component({ selector: 'app-version-parent', template: ` <h2>Source code version</h2> <button (click)="newMinor()">New minor version</button> <button (click)="newMajor()">New major version</button> <app-version-child [major]="major" [minor]="minor"></app-version-child> ` }) export class VersionParentComponent { major = 1; minor = 23; newMinor() { this.minor++; } newMajor() { this.major++; this.minor = 0; } }
最後成果如下圖
Parent listens for child event
子元件的EventEmitter財產是一個輸出的屬性,通常用@Output來宣告,如下:
import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-voter', template: ` <h4>{{name}}</h4> <button (click)="vote(true)" [disabled]="voted">Agree</button> <button (click)="vote(false)" [disabled]="voted">Disagree</button> ` }) export class VoterComponent { @Input() name: string; @Output() onVoted = new EventEmitter<boolean>(); voted = false; vote(agreed: boolean) { this.onVoted.emit(agreed); this.voted = true; } }
而父元件的內容如下:
import { Component } from '@angular/core'; @Component({ selector: 'app-vote-taker', template: ` <h2>Should mankind colonize the Universe?</h2> <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3> <app-voter *ngFor="let voter of voters" [name]="voter" (onVoted)="onVoted($event)"> </app-voter> ` }) export class VoteTakerComponent { agreed = 0; disagreed = 0; voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto']; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; } }
結果如圖:
Parent interacts with child via local variable
下面是範例,將子元件使用#來宣告為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> <app-countdown-timer #timer></app-countdown-timer>
Parent and children communicate via a service
服務內容:
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; @Injectable() export class MissionService { // Observable string sources private missionAnnouncedSource = new Subject<string>(); private missionConfirmedSource = new Subject<string>(); // Observable string streams missionAnnounced$ = this.missionAnnouncedSource.asObservable(); missionConfirmed$ = this.missionConfirmedSource.asObservable(); // Service message commands announceMission(mission: string) { this.missionAnnouncedSource.next(mission); } confirmMission(astronaut: string) { this.missionConfirmedSource.next(astronaut); } }
父元件內容
import { Component } from '@angular/core'; import { MissionService } from './mission.service'; @Component({ selector: 'app-mission-control', template: ` <h2>Mission Control</h2> <button (click)="announce()">Announce mission</button> <app-astronaut *ngFor="let astronaut of astronauts" [astronaut]="astronaut"> </app-astronaut> <h3>History</h3> <ul> <li *ngFor="let event of history">{{event}}</li> </ul> `, providers: [MissionService] }) export class MissionControlComponent { astronauts = ['Lovell', 'Swigert', 'Haise']; history: string[] = []; missions = ['Fly to the moon!', 'Fly to mars!', 'Fly to Vegas!']; nextMission = 0; constructor(private missionService: MissionService) { missionService.missionConfirmed$.subscribe( astronaut => { this.history.push(`${astronaut} confirmed the mission`); }); } announce() { let mission = this.missions[this.nextMission++]; this.missionService.announceMission(mission); this.history.push(`Mission "${mission}" announced`); if (this.nextMission >= this.missions.length) { this.nextMission = 0; } } }
子元件內容
import { Component, Input, OnDestroy } from '@angular/core'; import { MissionService } from './mission.service'; import { Subscription } from 'rxjs/Subscription'; @Component({ selector: 'app-astronaut', template: ` <p> {{astronaut}}: <strong>{{mission}}</strong> <button (click)="confirm()" [disabled]="!announced || confirmed"> Confirm </button> </p> ` }) export class AstronautComponent implements OnDestroy { @Input() astronaut: string; mission = '<no mission announced>'; confirmed = false; announced = false; subscription: Subscription; constructor(private missionService: MissionService) { this.subscription = missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; this.announced = true; this.confirmed = false; }); } confirm() { this.confirmed = true; this.missionService.confirmMission(this.astronaut); } ngOnDestroy() { // prevent memory leak when component destroyed this.subscription.unsubscribe(); } }
結果如下圖:
更多本篇完整範例請見: live example / download example