本篇延續上篇的實作 components ,用商家管理的功能來說明 Container components 與 Presentational components 之間用 @Input()
@Output()
溝通
與上一篇大同小異,不一樣的地方是在這裡新增了 models 的資料夾與 model 的檔案 (第 3、16、17 行),需要 model 是因為我們實作的功能會有有關商家的 Data,我們要定義商家這個型別有什麼屬性,讓我們在開發上能用到 TypeScript 帶來的好處。
cd ./merchant
touch index.ts
mkdir components containers models
cd ./containers
ng g c merchant-list
cd ../components
ng g c merchant-item
ng g c merchant-edit
cd ../models
ng g class merchant --type=model
將 merchant-list component 與 merchant 模組匯出與上一篇類似,這裡就不一一說明,如果不太知道怎麼做可以看今日程式碼
再來是修改一下 app.component.html 檔案,把 app-merchant-list
加上去
<app-nav class="mat-elevation-z6"></app-nav>
<app-layout>
<app-merchant-list></app-merchant-list>
</app-layout>
這裡為 Merchant 的 class 定義裡了這些屬性,分別是識別碼、名稱、商家地址、商家電話、商家網站、商家 LOGO
export class Merchant {
id: string;
name: string;
adress: string;
phone: string;
website: string;
logo: string;
}
依照剛剛在產生這 component 放的資料夾位置,大家應該可以猜到他會被定義成一個 Container component。在這我會先給他儲存狀態與操作狀態的職責(將來這個狀態會交給 service),然後給這個 class 一些屬性與方法,分別為 merchant(各個商家的資料)、openEditModal(打開編輯商家資訊的跳窗)、createMerchant(新增新增商家)、updateMerchant(更新商家的資訊)、deleteMerchant(刪除商家),這次只先實作刪除商家。
// ... 省略
export class MerchantListComponent implements OnInit {
merchants: Merchant[] = fakeMerchants;
constructor() {}
ngOnInit(): void {}
openEditModal(mode, merchantId?): void {
console.log('mode', mode);
console.log('id', merchantId);
}
createMerchant(merchant): void {}
updateMerchant(merchant): void {}
deleteMerchant(merchantId): void {
this.merchants = this.merchants.filter(
(merchant) => merchant.id !== merchantId
);
}
}
merchant-list.component.html 中,因為畫面要顯示每個商家個別的資訊,所以要加入 app-merchant-item
這個 component,並用 Property binding (第 4 行)與 Event binding (第 5、6 行)的方式讓 components 溝通 (稍後再說明如何實作)
第 2 行的 *ngFor 為內建的 Structural directives,會依照模板(第 2~8 行)與屬性綁定的數據 (merchants) 建立一到多個物件
<div class="category-list">
<div *ngFor="let merchant of merchants" class="category-list-item">
<app-merchant-item
[merchant]="merchant"
(openModal)="openEditModal('edit', $event)"
(deleteItem)="deleteMerchant($event)"
></app-merchant-item>
</div>
<div class="category-list-item add-button" (click)="openEditModal('create')">
<p>+ 新增店家</p>
</div>
</div>
merchant-item 是一個 presentational component ,他的職責在顯示 UI 與把使用者事件傳出去,在他的 class 雖然有 editMerchant、deleteMerchant 的兩個方法,實際上沒有實作的邏輯,還是藉由事件的方式把 merchantId 傳出去(第 4、5、12、16 行);而這裡的 merchant 屬性(第 3 行)則是由父 component 傳值進來的。 html 與 css 這邊就不再多說明了,主要是一些畫面呈現與使用 material 套件的技巧。
// ... 省略
export class MerchantItemComponent implements OnInit {
@Input() merchant: Merchant;
@Output() editItem = new EventEmitter<string>();
@Output() deleteItem = new EventEmitter<string>();
constructor() {}
ngOnInit(): void {}
editMerchant(merchantId?: string): void {
this.editItem.emit(merchantId);
}
deleteMerchant(merchantId: string): void {
this.deleteItem.emit(merchantId);
}
}
components 的概念就介紹到這裡,這是今天的程式碼。題外話,對於完全沒接觸過 Angular 的人來說可能會有點難消化,不過說實在,Angular 從零開始教學的類似主題已有很多厲害的大大整理出來過了,這邊想透過另一種方式來介紹 Angular 的各個角色。再來下一篇會介紹 service 是做什麼用的。