1. 父組件向子組件傳遞信息
使用@Input
子組件的屬性用 @Input
進行修飾,在父組件的模板中綁定變量
例子:
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'my-input',
template: `
<h1>I am your father!</h1>
<p>{{content}}</p>
<input-child [content]="content"></input-child>
`
})
export class InputComponent implements OnInit {
content: string = 'May the force be with you';
ngOnInit() { }
}
@Component({
selector: 'input-child',
template: `
<h1>The skywalker.</h1>
<p>{{content}}</p>`
})
export class InputChildComponent {
@Input() content: string = '';
}
效果:
使用 setter 攔截輸入的屬性
在子組件中做一些修改,添加兩個私有字段:revertContent
、_content
,然后為 _content
添加 setter 和 getter
代碼
@Component({
selector: 'input-child',
template: `
<h1>The skywalker.</h1>
<p>{{content}}</p>
<p>{{revertContent}}</p>`
})
export class InputChildComponent {
revertContent: string;
_content: string = '';
@Input() set content(str: string) {
this._content = str;
this.revertContent = str.split(' ').reverse().join(' ');
}
get content() {
return this._content;
}
}
效果:
使用 ngOnChanged
攔截輸入的屬性
首先要修改一下 import 內容:
import { Component, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';
然后讓子組件實現 OnChanges
接口的 ngOnchanges
方法,修改后的子組件如下:
@Component({
selector: 'input-child',
template: `
<h1>The skywalker.</h1>
<p>{{content}}</p>
<p>{{revertContent}}</p>`
})
export class InputChildComponent implements OnChanges {
revertContent: string = 'default content';
_content: string = 'default content';
@Input() set content(str: string) {
this._content = str;
}
get content() {
return this._content;
}
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
if (changes['content'] !== undefined) {
let value = <string>(changes['content'].currentValue);
this.revertContent = value.split(' ').reverse().join(' ');
this.revertContent = `revert "${value}" to "${this.revertContent}"`;
}
}
}
ngOnChanges
是一個生命周期鈎子,下圖指出了這個方法的調用時機。
效果:
2. 子組件向父組件傳遞消息
子組件使用 EventEmitter<TEvent>
子組件中聲明一個 EventEmitter<TEvent>
屬性,然后在父組件的模板中添加這個事件的監聽器
代碼:
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'event',
template: `
<h1>I'm your father.</h1>
<p>recived damage: {{damage}}</p>
<event-child (onAttacked)="onAttackedHandler($event)"></event-child>`
})
export class EventComponent implements OnInit {
damage: number = 0;
onAttackedHandler(damage: number) {
this.damage += damage;
}
ngOnInit() { }
}
@Component({
selector: 'event-child',
template: `
<h1>The SkyWalker.</h1>
<button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
`
})
export class EventChildComponent {
private baseDamage: number = 100;
damage: number = 0;
@Output() onAttacked = new EventEmitter<number>();
attack() {
this.damage = Math.random() * this.baseDamage;
this.onAttacked.emit(this.damage);
}
}
使用本地變量
在父組件模板中使用 #xxx
這樣的本地變量綁定子組件,然后就可以在模板中直接使用子組件的屬性作為數據源
@Component({
selector: 'event',
template: `
<h1>I'm your father.</h1>
<p>recived damage: {{damage}}</p>
<p>last damage: {{child.damage}}</p>
<event-child #child (onAttacked)="onAttackedHandler($event)"></event-child>`
})
export class EventComponent implements OnInit {
damage: number = 0;
onAttackedHandler(damage: number) {
this.damage += damage;
}
ngOnInit() { }
}
使用 @ViewChild
父組件中使用 @ViewChild
獲取子組件的引用,然后使用方法同上
import { Component, OnInit, Output, EventEmitter, ViewChild } from '@angular/core';
@Component({
selector: 'event-child',
template: `
<h1>The SkyWalker.</h1>
<button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
`
})
export class EventChildComponent {
private baseDamage: number = 100;
damage: number = 0;
@Output() onAttacked = new EventEmitter<number>();
attack() {
this.damage = Math.random() * this.baseDamage;
this.onAttacked.emit(this.damage);
}
}
// 注意,這里我交換了父子組件的位置,現在父組件的定義放在了下面
@Component({
selector: 'event',
template: `
<h1>I'm your father.</h1>
<p>recived damage: {{damage}}</p>
<p>last damage: {{child.damage}}</p>
<event-child (onAttacked)="onAttackedHandler($event)"></event-child>`
})
export class EventComponent implements OnInit {
damage: number = 0;
@ViewChild(EventChildComponent) child: EventChildComponent;
onAttackedHandler(damage: number) {
this.damage += damage;
}
ngOnInit() { }
}
終極大招——組件間使用 service 通信
首先讓我們在文件的開頭定義一個 AttackService
:
import { Component, OnInit, Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Rx';
@Injectable()
export class AttackService {
// 用來產生數據流的數據源
private damageSource = new Subject<number>();
// 把數據流轉換成 Observable
damage$ = this.damageSource.asObservable();
attack(damage: number) {
// 把傷害輸入到數據流
this.damageSource.next(damage);
}
}
然后在父組件與子組件中訂閱這個數據流:
@Component({
selector: 'event-child',
template: `
<h1>The SkyWalker.</h1>
<button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
`
})
export class EventChildComponent {
private baseDamage: number = 100;
damage: number = 0;
constructor(private attackService: AttackService) {
}
attack() {
this.damage = Math.random() * this.baseDamage;
// 天行者調用 AttackService 產生傷害
this.attackService.attack(this.damage);
}
}
// 注意,這里我交換了父子組件的位置,現在父組件的定義放在了下面
@Component({
selector: 'event',
providers: [AttackService], // 向父組件注入 AttackService,這樣,父組件與子組件就能共享一個單例的 service
template: `
<h1>I'm your father.</h1>
<p>recived damage: {{damage}}</p>
<p>last damage: {{lastDamage}}</p>
<event-child></event-child>`
})
export class EventComponent implements OnInit {
damage: number = 0;
lastDamage: number = 0;
constructor(private attackService: AttackService) {
// 父組件訂閱來自天行者的傷害
this.attackService.damage$.subscribe(damage => {
this.lastDamage = damage;
this.damage += damage;
}, error => {
console.log('error: ' + error);
});
}
ngOnInit() { }
}
效果:
如果我們把 service 注冊到根模塊,那么,就可以在整個 app 中共享數據啦~