您好,登錄后才能下訂單哦!
這篇文章主要介紹Angualr組件間通信的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
Angualr 組件間通信
約定: 遵循Angular官方的說法,下文中的AngularJS代指1.x版本,Angular代指Angular2及以后的升級版本。
采用Angular(或者任意MV*)的前端框架開發(fā)單頁應(yīng)用(SPA)時,我們都可能會遇見如下的場景:
A組件和B組件之前需要相互通信,或是A路由狀態(tài)需要知道B路由狀態(tài)的信息等等業(yè)務(wù)需求。
這個時候就需要設(shè)計到采用一套合理的通信方案來解決數(shù)據(jù)同步,數(shù)據(jù)通信的問題。
AngularJS 組件間的數(shù)據(jù)通信
在AngularJS中,也就是Angular JS 1.x版本中,我們需要實現(xiàn)控制器間的通信,有很多種方案,常見的有:
1. 采用 SharedService, 利用共享的公共服務(wù)來實現(xiàn)數(shù)據(jù)交換。
AngularJS中的Service被設(shè)計成單例的,這為這一方案,提供來底層的實現(xiàn)可行性,這一方案也是被廣泛采用的。
2. 利用AngularJS提供的事件機制,$rootScope.$broadcast/ $scope.$emit 配合 $on 方法實現(xiàn)。
該方案的實現(xiàn)具備一定的局限性,比如:$emit方法只能向上傳遞事件,而不能實現(xiàn)向下的事件傳播。但是進行合理的搭配組合已經(jīng)基本夠用了。
3. 利用瀏覽器的SessionStorage或者LocalStorage進行數(shù)據(jù)交換。
由于瀏覽器提供的這兩類本地存儲方案都提供了相應(yīng)的storage事件,所以我們也可以使用該方案進行數(shù)據(jù)交換。使用該方案是應(yīng)該注意及時清理無關(guān)數(shù)據(jù)。
4. 采用響應(yīng)式的編程思想或者觀察者模式的應(yīng)用。關(guān)于這一類實現(xiàn),需要經(jīng)歷一個編程思想的轉(zhuǎn)變,之后會通過專門的文章進行記錄。
5. 自身實現(xiàn)共享變量池。這個難度比較大,沒有一定的設(shè)計能力并不推薦。
由于AngularJS并不是本文的重點,所以這里只簡單的提一下。后面介紹的Angular的方案也有許多可以通用的地方。
Angular 組件間的數(shù)據(jù)通信
SharedService
共享服務(wù)方案在新的Angular中依然可以使用,而且無需額外的學(xué)習(xí)成本。這里在之前的學(xué)習(xí)筆記里有記錄,不再紀錄了。
SharedService 搭配 RxJS
聽說 SharedService 和 RxJS 搭配更實用哦!這里的學(xué)習(xí)成本在于 RxJS ,RxJS只是 Rx思想的JS實現(xiàn)。這里強烈推薦學(xué)習(xí)Rx編程思想, 她的學(xué)習(xí)收益比絕對讓你想象不到。
RxJS不需要我們不斷的手動去SharedService中檢查數(shù)據(jù)是否產(chǎn)生了變更,而是在數(shù)據(jù)有變化時,主動將變動的數(shù)據(jù)推送給感興趣的任何訂閱者。
舉個栗子:
我們有一份隨時可能會發(fā)生變動的數(shù)據(jù)存在了服務(wù)A中,在沒有使用RxJS(或是類似框架/庫)的情況下,我們想要知道數(shù)據(jù)的變化, 我們可能會采用輪詢的機制去不斷的詢問服務(wù)A,我們關(guān)心的數(shù)據(jù)是否發(fā)生了變化,如果變化了就做出相應(yīng)的動作。我們處于一種盲目的主動狀態(tài)。
更高級一點的實現(xiàn),就是我們可能把服務(wù)A實現(xiàn)稱為一個可被觀察的對象,這樣我們便能通過觀察服務(wù)A的狀態(tài)來獲取數(shù)據(jù)的變動。
現(xiàn)在我們來使用RxJS實現(xiàn),RxJS現(xiàn)在可以理解為更高級的觀察者模式實現(xiàn)。當使用來RxJS之后,在服務(wù)A中的數(shù)據(jù)發(fā)生變化時,服務(wù)A會主動 將變動的數(shù)據(jù)推送給每一個感興趣的‘消費者',這些消費者處于被動接受數(shù)據(jù),自主處理數(shù)據(jù)的狀態(tài)中。RxJS不同于普通觀察者模式的地方在于, 它提供來一系列的數(shù)據(jù)操作方法,比如:filter, map等等。這樣就便于我們更加精細的處理數(shù)據(jù)。
下面通過一段簡單的示例代碼來感受一下:
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; @Injectable() export class DataService { private data: any; private subject: Subject<any> = new Subject<any>(); setData(data: any): void { this.data = data; this.subject.next(data); } getData(): Observable<any> { return this.subject.asObservable(); } }
上面的代碼中,我們簡單的實現(xiàn)了一個可觀察的數(shù)據(jù)服務(wù),下面我們來使用這個服務(wù)
import { Component, OnInit } from '@angular/core'; import { DataService } from './shared/Dataservice'; @Component({ moduleId: module.id, selector: '<my-component></my-component>', templateUrl: '.name.component.html', providers: [DataService] }) export class MyComponent implements OnInit { constructor(private dataService: DataService) {} ngOnInit() { // Will invoke data handler everytime // when other component use the setter this.dataService.setData() this.dataService.getData().subscribe(data => console.log(data)); } }
使用Angular底層提供的 @Input 和 @Output 裝飾器來實現(xiàn)組件間的通信服務(wù)
新的Angular采用Web Component的方式進行組件的封裝,并將組件間的組織關(guān)系設(shè)計稱為樹狀結(jié)構(gòu)。這為應(yīng)用數(shù)據(jù)的流向,管理提供了良好的支持, 也使得我們可以在Angular應(yīng)用中使用一些別的庫,比如: Redux 思想?;谶@些設(shè)計理念,Angular為指令提供了更為強大的功能,組件也是指令。
采用 @Input 修飾屬性,實現(xiàn) parent -> child 組件間的通信
下面的示例代碼將展示如何設(shè)置一個組件的Input屬性
import { Component, Input, OnInit } from '@angular/core'; @Component({ moduleId: module.id, selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent implements OnInit { @Input() name: string; constructor() { } ngOnInit() { } }
上面的代碼中,我們實現(xiàn)了一個名叫ChildComponent的組件,這個組件的有一個采用@Input裝飾器修飾的屬性:name。
下面我們將展示如何使用這個這個組件,并為這個Input屬性賦值。
import { Component, OnInit } from '@angular/core'; import { ChildComponent } from './child-component'; @Component({ moduleId: module.id, selector: 'parent-component', template: `<child-component [name]="childName"></child-component>`, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = 'StevenShen'; } }
上面的代碼實現(xiàn)中,在父組件中,我們?yōu)樽咏M件的Input屬性設(shè)置了父組件中的特定值。關(guān)鍵點在如下片段:
<child-component [name]="childName"></child-component>
Angular在進行AOT操作時,會將特定的值注入給ChildComponent中。
如果你在CodePen,或是自己的本地實驗上面的代碼你會發(fā)現(xiàn),和AngularJS的指令中采用'@', ‘=', ‘&'等修飾的屬性不一樣的地方。
當父組件中的childName發(fā)生變化時,ChildComponent中的name屬性并沒有感知到變化。這是怎么了,是不是感覺新版的Angular在 和我們開玩笑,wtf?。?!內(nèi)心的表情是這樣的 ○| ̄|_ 。(感覺一篇學(xué)習(xí)筆記開始被寫的畫風(fēng)突變了。。。)
將父組件的屬性變化映射到子組件中
上一小節(jié)的實現(xiàn),雖然在初始化子組件時,我們可以將父組件的值反饋到子組件中。但是,初始化完成后,父組件中相關(guān)屬性的變化卻不能被子組件感知。
這無疑是讓我們內(nèi)心崩潰的。為什么和AngularJS不一樣了???別急,下面我們將來提供解決方案。
利用Angular提供的組件生命周期鉤子函數(shù)ngOnChanges來監(jiān)聽輸入屬性值的變化
需要實現(xiàn)讓子組件感知到父組件中相關(guān)屬性的變化,我們需要對Angualr組件的生命周期有一定的了解,采用Angular提供的組件生命周期的鉤子函數(shù), 進行組件間數(shù)據(jù)的同步。(關(guān)于Angualr組件的生命周期,之后會有相關(guān)的學(xué)習(xí)筆記整理。到時候在加上鏈接。)這里直接上代碼:
import { Component, Input, SimpleChanges } from '@angular/core'; @Component({ moduleId: module.id, selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { @Input() name: string; ngOnChanges(changes: SimpleChanges) { this.name = changes['childName'].currentValue; } }
采用ES5中的getter和setter方法進行輸入屬性的監(jiān)聽
在ES5中,我們在定義一個對象的屬性時,可以通過Object.defineProperty方法為一個對象的屬性設(shè)置關(guān)聯(lián)的getter和setter方法, 當我們進行這一操作后,以后該對象上的相關(guān)屬性的讀取和賦值操作,都會調(diào)用相應(yīng)的getter/setter方法進行預(yù)處理或改變操作結(jié)果。
同樣的道理,在Angular中,我們通過設(shè)置和使用一個輸入屬性的setter方法,便可以攔截到父組件中相關(guān)值的變化,并進行特定的數(shù)據(jù)處理。
這種方法比較直觀,直接上代碼:
父組件的代碼實現(xiàn):
import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'name-parent', template: ` <h3>Master controls {{names.length}} names</h3> <name-child *ngFor="let name of names" [name]="name"></name-child> ` }) export class ParentComponent { names = ['StevenShen', ' ', ' lei ']; }
子組件的代碼實現(xiàn)
import { Component, Input } from '@angular/core'; @Component({ moduleId: module.id, selector: 'name-child', template: `<h4>"{{name}}"</h4>` }) export class ChildComponent { name: string = 'default name'; @Input() set name(name: string) { this.name = (name && name.trim()) || 'default name'; } get name() { return this.name; } }
采用 @Output 修飾屬性,實現(xiàn) child -> parent 組件間的通信
新版的 Angular 中,子組件和父組件間的通信,采用來事件的機制。這樣的設(shè)計有助于組件的復(fù)用和代碼的解耦;
我們不需要過多的關(guān)心組件的具體實現(xiàn),我們只需要知道一個組件它接收哪些數(shù)據(jù),并產(chǎn)生哪些輸出事件即可。
直接上代碼直觀了解一下:
@Component({ moduleId: module.id, selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { @Input() name: string; @Output() say: EventEmitter<boolean> = new EventEmitter<boolean>(); ngOnChanges(changes: SimpleChange) { this.name = changes['childName'].currentValue; } speak() { this.say.emit(true); } }
子組件變更完成后,我們來變更父組件的代碼實現(xiàn)。
import { Component, OnInit } from '@angular/core'; import { ChildComponent } from './child-component'; @Component({ moduleId: module.id, selector: 'parent-component', template: `<child-component [name]="childName" (say)="isChildSpeak($event)"></child-component>`, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = 'StevenShen'; } isChildSpeak(isIt: boolean) { console.log('The child speak status is %s', isIt ? 'ture' : 'false'); } }
這樣一來就實現(xiàn)了父子組件間的通信了。
但是這樣的實現(xiàn)存在一定的局限性:父組件不能使用數(shù)據(jù)綁定來讀取子組件的屬性或調(diào)用子組件的方法.
通過 @ViewChild 獲取組件的控制器/模版進行組件間的通信
除開使用 @Input 和 @Output 修飾器搭配Angular的生命周期鉤子函數(shù)進行組件間通信。 我們還可以采用@ViewChild來進行不同組件間的通信,而不僅僅局限于父子組件間的通信。同時,采用@ViewChild的方式, 我們可以獲得更為精細的組件控制權(quán)限,比如在父組件中讀取子組件的屬性值或調(diào)用子組件的方法。我們依然采用上面的代碼來進行改造。
對于ChildComponent組件的變更:
import { Component } from '@angular/core'; @Component({ moduleId: module.id, selector: 'child-component', template: `I'm {{ name }}` }) export class ChildComponent { public name: string; speak() { console.log('say something whitout EventEmitter'); } }
對于ParentComponent組件的變更:
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; import { ChildComponent } from './child-component.ts'; @Component({ moduleId: module.id, selector: 'parent-component', // attention #childCmp tag template: ` <child-component #childCmp></child-component> <button (click)="child.name = childName"></button> `, // This is unnecessary when installing ChildComponent within Root NgModule directives: [ ChildComponent ] }) export class ParentComponent implements OnInit, AfterViewInit { @ViewChild('childCmp') childCmp: ElementRef; constructor() { } ngOnInit() { this.childCmp.name = 'StevenShen'; } ngAfterViewInit() { this.childCmp.speak(); } }
通過上面的代碼改造,我們同樣可以實現(xiàn)不同組件間的通信,而且這樣的組件通信已經(jīng)不僅僅局限于父子組件間的通信了。
以上是“Angualr組件間通信的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。