Angular中元件間的通訊
元件表示了Angular中的構建塊,在Angular應用中每一個視覺化元素都是由元件構成的。基於元件架構的好處是它更像JavaScript的function函式一樣,如果某一個程式碼段變得越來越複雜或者需要處理的業務越來越多,你可以把它進行拆分,使每個程式碼段只完成一項任務。
當我們把一個元件拆分成多個更小的元件的時候,需要保證他們之間可以進行資料傳遞,以確保在我們的應用中所有的資料都是同步的,這就意味著正確的元件之間的通訊變得十分必要。幸運的是Angular為我們提供了這樣的工具。
傳值給元件
在Angular中,當父元件需要傳值給子元件時,我們使用@Input來進行標識。假設我們構建了一個應用,在其中的一個頁面上我們需要展示評論列表。AppComponent負責載入評論資料的陣列,我們將把每個評論資料傳送給評論元件。
我們將允許使用@Input()把評論資料傳遞給子元件,以下是評論元件的程式碼:
@Component({
selector: 'comment',
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.css']
})
export class CommentComponent {
@Input() comment;
}
現在我們可以在我們程式碼的其他部分呼叫這個元件,並且給它傳遞期望傳遞的資料。如下面程式碼這樣:
<comment [comment]="comment"></comment>
理解語法
首先,我們有一個元件選擇器:<comment></comment>
組合概念
為了實現像上圖中我們看到的評論列表,我們要組合一些Angular的概念,包括*ngFor。假設我們能夠把評論資料載入到我們元件類的this.comments屬性中。
<comment
*ngFor="let comment of comments"
[comment]="comment"></comment>
最後的結果看起來像這樣的:
捕獲子元件事件
現在我們瞭解瞭如何把資料傳遞給評論元件,那麼怎麼刪除一個元件並把它從列表中移除呢?這是一個非常棘手的問題,因為資料是在父元件當中。
解決這個問題的一種方式是使用@Output(),它能夠讓子元件觸發可以讓父元件通過EventEmitter進行捕獲的事件。
為了使子元件與它們的父元件進行通訊,第一步是使用@Output修飾符為我們的元件新增一個新的類屬性。
@Component({
selector: 'comment',
templateUrl: './comment.component.html'
})
export class CommentComponent {
@Input() private comment;
@Output() private onDelete = new EventEmitter();
deleteComment() {
this.onDelete.emit(this.comment);
}
}
在CommentComponent模板中,當我們點選刪除按鈕時,呼叫deleteComment()方法,這時父元件開始捕捉這些事件。
<button (click)="deleteComment()">Delete</button>
從父元件中捕捉事件
在AppComponent中,我們需要一個新的方法來處理刪除評論的動作。
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
/**[code omitted]**/
onCommentDelete(comment) {
// logic to remove comment from comments array
}
}
按照我們的看法,我們只需要告訴Angular,當onDelete事件被觸發的時候,呼叫onCommentDelete()方法即可。
<comment
*ngFor="let comment of comments"
[comment]="comment"
(onDelete)="onCommentDelete($event)"></comment>
這是我們的應用看起來像我們執行了刪除功能的樣子
另一種獲取資料的方式
到目前為止,我們僅僅涉及了元件通訊中父/子層級元件的通訊問題。這些可能適用於我們大部分的需求,當隨著應用的不斷壯大,很難再去維護這種在父子層級上的資料傳遞,所以對於一個更大的應用來說,使用“資料儲存”來減少單個元件的工作負載是比較合理的。資料儲存以作為單個元件的中心倉庫方式進行工作,當應用程式的某一部分需要元件時,他們被進行呼叫。為了替代手動地向下傳遞資料到子鏈,單個元件能夠在資料儲存中訂閱他們需要的資料,減少父子元件之間傳遞資料的負擔。
如果你熟悉React,這個問題是由Redux所解決的。但是在Angular中,我們也有一個可選擇的庫,叫做ngrx,它的靈感來自於Redux。在這兩個庫之間有細微的關鍵區別:types and observables。ngrx庫十分依賴於TypeScript的生態系統,它比Redux需要更多的樣板檔案,不過這也使跟蹤除錯變得更加容易。
當使用ngrx時,我們的狀態是一個單一不可變的資料結構。為了改變我們的狀態,我們通過呼叫動作函式來進行。反過來,那些動作告訴我們的reducers(它們是由一些單純的函式所組成的),哪些部分的狀態能夠被改變。因此,我們的應用現在將有下一個版本的狀態並且所有訂閱它的元件將馬上收到新的資料。當元件收到新的資料時,它們也會進行自動的渲染,使我們渲染的檢視與我們儲存中的資料總是保持一致。
對於開發來說這意味著什麼呢?
從概念上來說的意義是能夠把資料從外部資料來源中直接傳遞給元件,而不用通過父子元件關係的形式,那麼對於開發來說實際意義又是什麼呢?
把資料直接傳遞給元件對於大型應用來說是十分有意義的,因為元件需要處理更多的任務,新增額外的任務,如一直跟蹤傳送資料給子元件,會變得複雜。不過把傳遞資料的負擔轉移到外部資料儲存並不是必要的,以適合自身的方式來維護你的大型Angular應用才是很有意義的!
在上面的示例中,外部儲存(非圖示)將有多少評論可用的資訊,“2個評論可用傳遞給SidebarComponent以及將實際的評論傳遞給CommentList元件,這次的資料傳遞完全的繞過了父AppComponent。