組件就像零散的積木识脆,我們需要把這些積木按照一定的規(guī)則拼裝起來甸祭,而且要讓它們互相之間能進(jìn)行通訊缕碎,這樣才能構(gòu)成一個(gè)有機(jī)的完整系統(tǒng)。
在真實(shí)的應(yīng)用中池户,組件最終會(huì)構(gòu)成樹形結(jié)構(gòu)咏雌,就像人類社會(huì)中的家族樹一樣:
在樹形結(jié)構(gòu)里面凡怎,組件之間有幾種典型的關(guān)系:父子關(guān)系、兄弟關(guān)系处嫌、沒有直接關(guān)系栅贴。
相應(yīng)地斟湃,組件之間有以下幾種典型的通訊方案:
直接的父子關(guān)系:父組件直接訪問子組件的 public 屬性和方法熏迹。
直接的父子關(guān)系:借助于 @Input 和 @Output 進(jìn)行通訊
沒有直接關(guān)系:借助于 Service 單例進(jìn)行通訊。
利用 cookie 和 localstorage 進(jìn)行通訊凝赛。
利用 session 進(jìn)行通訊注暗。
無論你使用什么前端框架,組件之間的通訊都離開不以上幾種方案墓猎,這些方案與具體框架無關(guān)捆昏。
直接調(diào)用
對于有直接父子關(guān)系的組件,父組件可以直接訪問子組件里面 public 型的屬性和方法毙沾,示例代碼片段如下:
<child #child></child>
<button (click)="child.childFn()" class="btn btn-success">調(diào)用子組件方法</button>
顯然骗卜,子組件里面必須暴露一個(gè) public 型的 childFn 方法,就像這樣:
public childFn():void{ console.log("子組件的名字是>"+this.panelTitle);}
以上是通過在模板里面定義局部變量的方式來直接調(diào)用子組件里面的 public 型方法左胞。在父組件的內(nèi)部也可以訪問到子組件的實(shí)例寇仓,需要利用到 @ViewChild 裝飾器,示例如下:
@ViewChild(ChildComponent)private childComponent: ChildComponent;
關(guān)于 @ViewChild 在后面的內(nèi)容里面會(huì)有更詳細(xì)的解釋烤宙。
很明顯遍烦,如果父組件直接訪問子組件,那么兩個(gè)組件之間的關(guān)系就被固定死了躺枕。父子兩個(gè)組件緊密依賴服猪,誰也離不開誰,也就都不能單獨(dú)使用了拐云。所以罢猪,除非你知道自己在做什么,最好不要直接在父組件里面直接訪問子組件上的屬性和方法叉瘩,以免未來一改一大片膳帕。
@Input 和 @Output
我們可以利用 @Input 裝飾器,讓父組件直接給子組件傳遞參數(shù)房揭,子組件上這樣寫:
@Input()public panelTitle:string;
父組件上可以這樣設(shè)置 panelTitle 這個(gè)參數(shù):
<child panelTitle="一個(gè)新的標(biāo)題"></child>
@Output 的本質(zhì)是事件機(jī)制备闲,我們可以利用它來監(jiān)聽子組件上派發(fā)的事件,子組件上這樣寫:
@Output()public follow=new EventEmitter();
觸發(fā) follow 事件的方式如下:
this.follow.emit("follow");
父組件上可以這樣監(jiān)聽 follow 事件:
<child (follow)="doSomething()"></child>
我們可以利用 @Output 來自定義事件捅暴,監(jiān)聽自定義事件的方式也是通過小圓括號恬砂,與監(jiān)聽 HTML 原生事件的方式一模一樣。
利用 Service 單例進(jìn)行通訊
如果你在根模塊(一般是 app.module.ts)的 providers 里面注冊一個(gè) Service蓬痒,那么這個(gè) Service 就是全局單例的泻骤,這樣一來我們就可以利用這個(gè)單例的 Service 在不同的組件之間進(jìn)行通訊了。
- 比較粗暴的方式:我們可以在 Service 里面定義 public 型的共享變量,然后讓不同的組件都來訪問這塊變量狱掂,從而達(dá)到共享數(shù)據(jù)的目的演痒。
- 優(yōu)雅一點(diǎn)的方式:利用 RxJS,在 Service 里面定義一個(gè) public 型的 Subject(主題)趋惨,然后讓所有組件都來subscribe(訂閱)這個(gè)主題鸟顺,類似于一種“事件總線”的效果。
實(shí)例代碼片段:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
/**
* 用來充當(dāng)事件總線的Service
*/
@Injectable()
export class EventBusService {
public eventBus:Subject = new Subject();
constructor() { }
}
import { Component, OnInit } from '@angular/core';
import { EventBusService } from '../service/event-bus.service';
@Component({
selector: 'child-1',
templateUrl: './child-1.component.html',
styleUrls: ['./child-1.component.css']
})
export class Child1Component implements OnInit {
constructor(public eventBusService:EventBusService) { }
ngOnInit() {
}
public triggerEventBus():void{
this.eventBusService.eventBus.next("第一個(gè)組件觸發(fā)的事件");
}
}
import { Component, OnInit } from '@angular/core';
import { EventBusService } from '../service/event-bus.service';
@Component({
selector: 'child-2',
templateUrl: './child-2.component.html',
styleUrls: ['./child-2.component.css']
})
export class Child2Component implements OnInit {
public events:Array=[];
constructor(public eventBusService:EventBusService) {
}
ngOnInit() {
this.eventBusService.eventBus.subscribe((value)=>{
this.events.push(value+"-"+new Date());
});
}
}
利用 cookie 或者 localstorage 進(jìn)行通訊
示例代碼片段:
public writeData():void{
window.localStorage.setItem("json",JSON.stringify({name:'大漠窮秋',age:18}));
}
var json=window.localStorage.getItem("json");
// window.localStorage.removeItem("json");
var obj=JSON.parse(json);
console.log(obj.name);
console.log(obj.age);
很多朋友寫 Angular 代碼的時(shí)候出現(xiàn)了思維定勢器虾,總感覺 Angular 會(huì)封裝所有東西讯嫂,實(shí)際上并非如此。比如 cookie兆沙、localstorage 這些東西都可以直接用原生的 API 進(jìn)行操作的欧芽。千萬別忘記原生的那些 API 啊,都能用的葛圃!
利用 session 進(jìn)行通訊
小結(jié)
組件間的通訊方案是通用的千扔,無論你使用什么樣的前端框架,都會(huì)面臨這個(gè)問題库正,而解決的方案無外乎本文所列出的幾種曲楚。
本節(jié)完整可運(yùn)行的實(shí)例代碼請參見這里 請檢出 communication 分支。
大漠原文 诀诊,請付費(fèi)支持http://gitbook.cn/gitchat/column/59dae2081e6d652a5a9c3603/topic/59dc78a41e6d652a5a9c7aa3#writeCommentDiv