今天來利用 Angular Material 的 Table 元件,建立工作事項的資料表清單元件。
在使用 Angular Material 的時候,可以利用 Angular Schemaics 來建立如表格、樹狀元件等 Material 元件。
ng generate @angular/material:table task-table
因此,透過上面指令來建立工作事項的資料表後,可以看到 Schemaics 除了建立元件的檔案外,也新增了一個 DataSource 的程式碼,這是 Material 實作資料清單的基本架構。
Data Source 的定義可以把清單的介面與資料拆分處理,無論靜態的資料陣列或是動態的 Observable 都可以當作資料來源。
在 Schemaics 所產生的程式碼裡,會幫我們定義一個讓資料表使用項目介面,我們可以依需求來修正所使用的資料對象。另外,為了讓資料來源可以完成獨立於介面,我會刪除 Schemaics 在 DataSource 所使用到的 paginator
與 sort
元件實體參數。
export class TaskDataSource extends DataSource<TaskItem> {
connect(): Observable<TaskItem[]> {}
disconnect(): void {}
}
如上面程式中,資料來源來繼承 @angular/cdk/collection
裡的 DataSource
類別,我們需要去定義此類別的 connect()
與 disconnect()
兩個方法,前者會回傳一 Observable 型別資料,讓我們定義當某些資料更改時,需要重新取得資料的邏輯;後者則是讓元件被銷毀時,所需要的作業邏輯。
export class TaskDataSource extends DataSource<TaskItem> {
readonly tasks = signal<TaskItem[]>([]);
private readonly tasks$ = toObservable(this.tasks);
constructor(tasks: TaskItem[]) {
super();
this.tasks.set(tasks);
}
connect(): Observable<TaskItem[]> {
return this.tasks$;
}
}
我們可以如上面程式,以靜態陣列資料作為資料來源;也可以在建立資料來源實體時,傳入對應的資料服務實體,透過與後端取得所需要的資料。
export class TaskDataSource extends DataSource<TaskItem> {
constructor(private readonly tasksService: TaskService) {
super();
}
connect(): Observable<TaskItem[]> {
return this.tasksService.getList();
}
}
在引用 MatTableModule
模組之後,就可以使用 <mat-table>
標籤,或是在 <table>
裡加入 mat-table
指令元件,並指定資料表的資料來源。
<table mat-table [dataSource]="dataSource" aria-label="Elements">
</table>
Angular Material Table 把資料表結構拆分成欄位定義以及資料表頭、表身與表尾定義兩個部分。首先,如下面程式,會利用 matColumnDef
來定義資料欄位,其中 mat-header-cell
與 matHeaderCellDef
用以定義欄位標題,而 mat-cell
與 matCellDef
則是定義欄位內容。
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>編號</th>
<td mat-cell *matCellDef="let row">{{ row.id }}</td>
</ng-container>
上面程式只定義此資料表會有哪些資料欄位,真正會顯示的欄位定義則會在 <tr>
中使用 mat-header-row
與 mat-row
進行設定。當我們需要變更欄位顯示或是順序,都可以透過操控 displayedColumns
這個屬性值來實現。
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
Material 的 Table 元件允許設定多列的表頭列、內容列與表尾列。如下面程式,除了在欄位使用 mat-footer-cell
與 matFooterCellDef
定義各欄位的頁尾內容。
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>編號</th>
<td mat-cell *matCellDef="let row">{{ row.id }}</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
或是如下面程式,針對所需要的表首與表尾欄位單獨定義。
<ng-container matColumnDef="subject-desc">
<th mat-header-cell *matHeaderCellDef>工作事項主旨</th>
</ng-container>
<ng-container matColumnDef="content-desc">
<th mat-header-cell *matHeaderCellDef>工作事項詳細說明</th>
</ng-container>
<ng-container matColumnDef="total">
<td mat-footer-cell *matFooterCellDef colspan="3">共 18 筆工作事項</td>
</ng-container>
最後,在利用 mat-header-row
與 mat-footer-row
定義在表首與表尾實際需要顯示的欄位。
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr
mat-header-row
*matHeaderRowDef="['id-desc', 'subject-desc', 'content-desc']"
></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
<tr mat-footer-row *matFooterRowDef="displayedColumns"></tr>
<tr mat-footer-row *matFooterRowDef="['total']"></tr>
順帶一提,我們可以在欄位定義時,利用 sticky
與 stickyEnd
屬性來設定此欄位是否被固定在左邊或是右邊。
<ng-container matColumnDef="id" sticky="true">
<th mat-header-cell *matHeaderCellDef>編號</th>
<td mat-cell *matCellDef="let row">{{ row.id }}</td>
<td mat-footer-cell *matFooterCellDef></td>
</ng-container>
同樣的,可以在表頭與表尾的設定上使用 sticky
屬性來固定表頭或表尾的顯示。
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
明天我們在今天所建立的工作事項資料表元件中加入分頁與排序的需求。