angular-組件
@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;}
第二個@Input
為子組件的屬性名masterName
指定一個別名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" // 子組件的 @Input() hero: Hero 對應這裏的 [hero] [master]="master" // @Input(‘master‘) masterName 對應這裏的 【master】
></app-hero-child> ` }) export class HeroParentComponent { heroes = HEROES; master = ‘Master‘; }
通過setter截聽輸入屬性值的變化
使用一個輸入屬性的setter,以攔截父組件中值的變化,並采取行動。
子組件NameChildComponent
的輸入屬性name
上的這個setter,會trim掉名字裏的空格,並把空值替換成默認字符串
@Input() set name(name: string) { this._name = (name && name.trim()) || ‘<no name set>‘; } get name(): string { return this._name; }
通過ngOnChanges()來截聽輸入屬性值的變化
使用OnChanges
生命周期鉤子接口的ngOnChanges()
方法來監測輸入屬性值的變化並做出回應
當需要監視多個、交互式輸入屬性的時候,本方法比用屬性的setter更合適
父組件監聽子組件的事件(EventEmitter)
子組件暴露一個EventEmitter
屬性,當事件發生時,子組件利用該屬性emits
(向上彈射)事件。父組件綁定到這個事件屬性,並在事件發生時作出回應。
子組件的EventEmitter
屬性是一個輸出屬性,通常帶有@Output裝飾器,就像在VoterComponent
中看到的
子組件:
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: ` <app-voter *ngFor="let voter of voters" [name]="voter"
// onVoted 對應 子組件的@Output onVoted (onVoted)="onVoted($event)"> </app-voter> ` }) export class VoteTakerComponent { onVoted(agreed: boolean) { // agreed 就是子組件傳遞的參數 } }
子組件:點擊按鈕會觸發true
或false
(布爾型有效載荷)的事件。
父組件:VoteTakerComponent
綁定了一個事件處理器(onVoted()
),用來響應子組件的事件($event
)並更新一個計數器。
父組件調用子組件方法或者屬性
父組件不能使用數據綁定來讀取子組件的屬性或調用子組件的方法。但可以在父組件模板裏,新建一個本地變量來代表子組件,然後利用這個變量來讀取子組件的屬性和調用子組件的方法
-
父組件模板上調用子組件的方法:
子組件:
start() { } stop() { }
父組件:
template: ` <button (click)="timer.start()">Start</button> <button (click)="timer.stop()">Stop</button> <app-countdown-timer #timer></app-countdown-timer> `,
把本地變量(#timer
)放到(<countdown-timer>
)標簽中,用來代表子組件。這樣父組件的模板就得到了子組件的引用,於是可以在父組件的模板中訪問子組件的所有屬性和方法
-
父組件js 調用子組件的方法@ViemChild():
父組件:
import { AfterViewInit, ViewChild } from ‘@angular/core‘;
import { Component } from ‘@angular/core‘;
import { CountdownTimerComponent } from ‘./countdown-timer.component‘;
@Component({
selector: ‘app-countdown-parent-vc‘,
template: `
<button (click)="start()">Start</button>
<button (click)="stop()">Stop</button>
<div class="seconds">{{ seconds() }}</div>
<app-countdown-timer></app-countdown-timer>
`,
styleUrls: [‘../assets/demo.css‘]
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
seconds() { return 0; }
ngAfterViewInit() {
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
通過@ViewChild
屬性裝飾器,將子組件CountdownTimerComponent
註入到私有屬性timerComponent
裏面
組件元數據裏就不再需要#timer
本地變量了。而是把按鈕綁定到父組件自己的start
和stop
方法
然後Angular會調用ngAfterViewInit
生命周期鉤子,但這時候再更新父組件視圖的倒計時就已經太晚了。Angular的單向數據流規則會阻止在同一個周期內更新父組件視圖。我們在顯示秒數之前會被迫再等一輪
(子組件CountdownTimerComponent和原來一樣)
父組件和子組件通過服務來通訊???
父組件和它的子組件共享同一個服務,利用該服務在家庭內部實現雙向通訊。
該服務實例的作用域被限制在父組件和其子組件內。這個組件子樹之外的組件將無法訪問該服務或者與它們通訊。
這個MissionService
把MissionControlComponent
和多個AstronautComponent
子組件連接起來
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); } }
MissionControlComponent
提供服務的實例,並將其共享給它的子組件(通過providers
元數據數組),子組件可以通過構造函數將該實例註入到自身
父組件:MissionControlComponent
import { Component } from ‘@angular/core‘; import { MissionService } from ‘./mission.service‘; @Component({ selector: ‘app-mission-control‘, template: ` <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; } } }
子組件:AstronautComponent
AstronautComponent
也通過自己的構造函數註入該服務。由於每個AstronautComponent
都是MissionControlComponent
的子組件,所以它們獲取到的也是父組件的這個服務實例
mport { 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();
}
}
angular-組件