咱們知道Angular2應用程序其實是有不少父子組價組成的組件樹,所以,瞭解組件之間如何通訊,特別是父子組件之間,對編寫Angular2應用程序具備十分重要的意義,一般來說,組件之間的交互方式主要有以下幾種:框架
l 使用輸入型綁定,把數據從父組件傳到子組件函數
l 經過 setter 攔截輸入屬性值的變化this
l 使用 ngOnChanges 攔截輸入屬性值的變化spa
l 父組件監聽子組件的事件對象
l 父組件與子組件經過本地變量互動接口
l 父組件調用 ViewChild生命週期
l 父組件和子組件經過服務來通信事件
本文會經過講解着幾種方式來對組件之間的通訊作一個大體的介紹。ci
輸入型綁定指的是利用模板語法中的屬性型綁定方式,將父組件的數據傳遞到子組件對應的對象中,子組件中的對象通常使用@Input裝飾器來修飾,做爲數據的接受者,例如get
@Component({ selector: 'child', template: 'I am fron {{input}}' }) export class ChildComponent implements OnInit { @Input() input; constructor() { } ngOnInit() { } } @Component({ selector: 'parent', template: '<child [input]="data"></child>' }) export class ParentComponent implements OnInit { data: string; constructor() { } ngOnInit() { this.data = "parent"; } } |
以上的例子咱們能夠看出,存在父子兩個組件,在父組件ParentComponent中的模板中引入了<child [input]="data"></child>子組件,並將data數據經過屬性綁定的方式綁定到input屬性中傳入到子組件中,子組件中經過@Input()註解修飾input屬性來接收傳入的數據,並顯示在模板I am fron {{input}}中。
輸入型綁定是從父組件傳遞數據到子組件最多見的方式。
咱們知道,Angular2是一個MVVM的框架,當數據發生變化時可以同步顯示到模板視圖中,可使用一個輸入屬性的 setter 函數,以攔截父組件中值的變化,並採起行動。例如,咱們改造上面的例子,子組件中使用set,get重寫對應的綁定input屬性,當輸入值方式變化時輸出一個控制檯信息。
@Component({ selector: 'child', template: 'I am fron {{data}}' }) export class ChildComponent implements OnInit { _input:string; @Input() public set input(v : string) { this._input = v; console.log(v); } public get input() : string { return this._input; } constructor() { } ngOnInit() { } } |
除了上面說的setter函數能夠響應輸入數據的變化外,Angular2還提供了一個生命週期函數ngOnChanges 能夠監聽數據的變化。使用 OnChanges 生命週期鉤子接口的 ngOnChanges 方法來監測輸入屬性值的變化並作出迴應。咱們改造以上的子組件來響應對應的變化,在這個示例中,咱們監聽了輸入數據的變化,採起的對應動做僅僅是輸出對應的信息,固然你也能夠作不少其餘的事情。
@Component({ selector: 'child', template: 'I am fron {{data}}' }) export class ChildComponent implements OnInit, OnChanges { _input: string; @Input() public set input(v: string) { this._input = v; console.log(v); } public get input(): string { return this._input; } constructor() { } ngOnInit() { } ngOnChanges(changes: SimpleChanges) { console.log(changes); } } |
上面的集中方式都是父組件如何向子組件傳遞數據以及子組件如何監聽數據的變化,事件傳播則是子組件如何向父組件通訊的一種方式。子組件暴露一個 EventEmitter 屬性,當事件發生時,子組件利用該屬性 emits( 向上彈射 ) 事件。父組件綁定到這個事件屬性,並在事件發生時做出迴應。子組件的 EventEmitter 屬性是一個 輸出屬性 ,一般帶有 @Output 裝飾器 。
@Component({ selector: 'child', template: ` I am fron {{data}}<br /> <button id="out" (click)="click()">click for out</button> ` }) export class ChildComponent implements OnInit, OnChanges { _input: string; @Input() public set input(v: string) { this._input = v; console.log(v); } public get input(): string { return this._input; } @Output() output:EventEmitter<string> = new EventEmitter<string>(); click(){ this.output.emit("i am from child"); } constructor() { } ngOnInit() { } ngOnChanges(changes: SimpleChanges) { console.log(changes); } } @Component({ selector: 'parent', template: '<child [input]="data" (output)="output($event)"></child>' }) export class ParentComponent implements OnInit { data: string; constructor() { } ngOnInit() { this.data = "parent"; } output($event){ console.log($event); } } |
在上面的例子中,咱們在子組件ChildComponent添加了一個向外傳播的事件output:EventEmitter<string> = new EventEmitter<string>(),並添加了一個點擊的按鈕,當按鈕事件觸發時,就會調用output事件向父組件傳遞事件,並將數據做爲參數傳遞到父組件ParentComponent中,同時在父組件ParentComponent的模板<child [input]="data" (output)="output($event)"></child>中能夠看到,咱們使用模板語法中的事件綁定,綁定了output函數做爲對應事件的接受函數,當子組件output事件觸發是,父組件的函數就會獲得執行。
使用事件傳播來進行子組件對父組件之間的通訊是最多見的方式。
在模板語法中,咱們知道存在着本地變量這種語法,可使用本地變量來表明對應的組件。雖然父組件不能使用數據綁定來讀取子組件的屬性或調用子組件的方法。但能夠在父組件模板裏,新建一個本地變量來表明子組件,而後利用這個變量來讀取子組件的屬性和調用子組件的方法,不過這種使用方式只能在模板中使用,例如以下所示,改寫上面例子中的父組件模板,代碼以下。
咱們在ParentComponent組件中使用本地變量#child獲取了child組件的實例,這樣就能夠在模板中使用其屬性或者方法,例如child.input。
@Component({ selector: 'parent', template: '<child [input]="data" (output)="output($event)" #child></child>{{child.input}}' }) export class ParentComponent implements OnInit { data: string; constructor() { } ngOnInit() { this.data = "parent"; } output($event){ console.log($event); } } |
本地變量的方式是在父組件的模板中獲取子組件的實例,有木有其餘方式能夠在組件的類中獲取子組件的實例呢?答案是確定的,若是父組件的類須要讀取子組件的屬性值或調用子組件的方法,就不能使用本地變量方法。當父組件類 須要這種訪問時,能夠把子組件做爲ViewChild,注入到父組件裏面。例如,咱們改造上面的父組件的組件類,使用ViewChild來獲取子組件的實例,代碼以下:
@Component({ selector: 'parent', template: '<child [input]="data" (output)="output($event)" #child></child>{{child.input}}' }) export class ParentComponent implements OnInit { @ViewChild(ChildComponent) private childComponent: ChildComponent; data: string; constructor() { } ngOnInit() { this.data = "parent"; } output($event) { console.log($event); } } |