前言
在物件導向程式的領域,有個稱為 OCP ( Open Closed Principle ) 的原則,中文稱為開放封閉原則。這原則說的是,在進行物件導向程式設計時要能符合開放擴充但封閉修改的要素,這樣子才能把每個不同的物件獨立切開、互不干擾。
這段前言又跟這篇文章有什麼關係呢?
截至目前為止的結構
可整理出如下列表
- AppModule - 根模組
- AppComponent
- ArticleList
- ArticleHeader
- ArticleBody
- ArticleList
- AppComponent
而元件一層包一層的好處是,一次只要關注一個想要修改的地方就好了,不管應用程式多複雜透過元件化的架構,就可以把一份複雜的網頁切割成多個元件,每個元件只單純負責幾件事情就好。
製作刪除文章的功能
假使想要實作出刪除文章的按鈕,想要將這個按鈕放在 ArticleHeader 的 Template 內,點擊後能夠將刪除的資訊傳給父元件 ArticleList ,最後達成刪除文章的效果。
添加刪除按鈕
首先在 ArticleHeader 的 Template 內做出一顆按鈕。
1 | <header class="post-header"> |
但是這樣是沒辦法刪除的,因為資料是由父元件 ArticleList 傳遞給子元件 ArticleHeader ,子元件本身並不具有這份資料。
解決辦法是將要刪除哪筆篇文章的資料往上傳遞給父元件,透過父元件刪除該篇文章。
白話來說,以刪除文章這個功能而言:
- 子元件 ArticleHeader 扮演的角色是:透過點擊事件通知父元件哪一篇文章的刪除按鈕被點擊了。
- 接著父元件 ArticleList 接收到訊息後把對應資料刪除,重新透過 ngFor 渲染網頁。
註冊 ArticleHeader 內的事件
從父元件傳值給子元件是透過屬性繫結;從子元件傳值給父元件則必須透過事件繫結。
在 Angular 元件內要註冊一個事件必須先宣告一個屬性,同時宣告一個 @Output
裝飾器,跟之前介紹過的 @Input
一樣。
首先來到 ArticleHeader 的 class
內進行修改:
1 | import { Component, OnInit, Input, Output } from '@angular/core'; |
這樣就完成了 delete 事件的註冊,接著要將這個事件繫結到父元件上。
事件的名稱可以自由命名。
在父元件上繫結 delete 事件
剛才已經在子元件內註冊了 delete 事件,此時在 ArticleList 內已經可以從自動完成中看到對應的選項。
ArticleList 內的 Template
1 | <!-- Article START--> |
當接收到
delete
事件發出的通知時,就會觸發 ArticleList 內的doDelete()
方法,而且還要讓它傳入一個$event
參數,這樣才能知道使用者點擊的是哪一篇文章的刪除按鈕。
完成 ArticleHeader 內的 delete 事件
剛才雖然已經註冊 delete
事件,但還有很多細節尚待處理。
- 例如剛才宣告的
delete
屬性需要 new 一個 EventEmitter 物件- EventEmitter 是一個事件的發射器。
- EventEmitter 後面可以接
<any>
,代表要傳送的資料可以是任意型別
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; |
什麼時候發射 delete 的事件?自然是按下刪除按鈕的時候
回到 ArticleHeader 的 Template :
1 | <header class="post-header"> |
當點擊按鈕時透過點擊事件,觸發 ArticleHeader 內的
deleteArticle
方法。
因此我們必須實作deleteArticle
方法。
1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; |
當 deleteArticle
方法被觸發時,透過 delete.emit()
這個事件發射器,將 this.item
向父元件射出。
而父元件上的 $event
接收的資料就會是剛才被射出的 this.item
。
實作 doDelete 方法
回到 ArticleList 的 class
內,實作刪除文章。
1 | import { Component, OnInit } from '@angular/core'; |
因為 atticleData
屬性並沒有明確的型別,所以使用自動完成並沒有提示可以使用什麼方法,因此手動補上 Array<any>
代表這是一個可以傳入任何資料的陣列。
最後透過 ES6 的 filter 方法篩選被刪除的文章,達成本次需求。
搞定~測試看看吧!
小結
可以很明顯的看出,從子元件將資料傳遞給父元件是要透過比較多道手續的,但整體而言也不難理解,就花點時間好好地記住吧。