Angular Material (Components Cdk) 學習筆記 Table


refer : 

https://material.angular.io/cdk/table/overview

https://material.angular.io/components/table/overview

 

通常我們做 control panel 時會大量運用到 table 

尤其是處理 CRUD 時, table 更是神器

說到 table 就一定會附帶以下這些東西 

filter, search 

pagination

sort

show/hide/sort columns

select row

cell display 

Table 作為高復用組件, 必須要足夠抽象。

material 在這里也是一如往常的做出了數據和 ui 分離. 

這個概念和 angular directive form 是一樣的. 

數據方面的處理是交給 DataSource 這個對象來負責. 

這個對象監聽 filter, search, sort 等等數據的變動,然后對數據進行處理 (mat table data source 目前沒有支持遠程數據處理, 我們得自己實現)

然后 ui table 通過監聽 data source 來獲取新數據. 

各種小組件, mat-sort, mat-pagination 則是負責監聽 ui 操作.

所以是

data source 監聽操作小組件 -> 更新 source 

ui table 監聽 data source -> 渲染

這個就是大致上架構的設計啦,我們只能跟着做了

 

 

下面說說常用的東西. 

1. ui table

首先開一個"家“ 監聽 data source 

<table mat-table [dataSource]="dataSource" [trackBy]="trackByFn" >
</table>

然后是里面

<ng-container matColumnDef="cost">
  <th mat-header-cell *matHeaderCellDef> Cost </th>
  <td mat-cell *matCellDef="let data"> {{data.cost}} </td>
  <td mat-footer-cell *matFooterCellDef> {{totalCost}} </td>
</ng-container>

...

<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let myRowData; columns: columnsToDisplay"></tr>
<tr mat-footer-row *matFooterRowDef="columnsToDisplay"></tr>

定義每一個 column template

一個 column 又分為 3 個 template 展現,  header,body,footer

最后 3 行則是定義 row 的呈現. 

不是每一個 column 都有 3 個展現的, 有些只有 header, body, 有些則只有 footer, 沒有就不要定義就好了

row 的 columnsToDisplay 也不是全部一樣的, 有一些 columns 只需要展現在 footer, 那么它就只出現在 row footer 的 columnsToDisplay 就好了。

 

 

 

2. max-text-column, 這個就是一個方便啦. 因為大部分都是 display string 嘛.

<mat-text-column name="score"></mat-text-column>

源碼是很簡單的

@Component({
  moduleId: module.id,
  selector: 'mat-text-column',
  template: `
    <ng-container matColumnDef>
      <th mat-header-cell *matHeaderCellDef [style.text-align]="justify">
        {{headerText}}
      </th>
      <td mat-cell *matCellDef="let data" [style.text-align]="justify">
        {{dataAccessor(data, name)}}
      </td>
    </ng-container>
  `,
  encapsulation: ViewEncapsulation.None, 
  changeDetection: ChangeDetectionStrategy.Default,
})
export class MatTextColumn<T> extends CdkTextColumn<T> {
}

通過 CdkTextColumn @input 去修改 header or cell, 

@Input() headerText: string;
@Input() dataAccessor: (data: T, name: string) => string;

 

3. Paginator 

這個就是上面說的操作小組件啦, 

  <mat-paginator #matPaginator [pageSizeOptions]="[1, 10, 20]"></mat-paginator>

通過 #matPaginator 打標簽, 然后 controller 就可以用 ViewChild 找到它 

@ViewChild('matPaginator', { read: MatPaginator, static: false })
matPaginator: MatPaginator;

這里我用 static false,大部分情況 true 是 ok 的, 不熟悉的人可以看看 angular 8.0 了解一下.

ngAfterViewInit() {
  this.dataSource.paginator = this.matPaginator;
}

然后就是讓它與 data source 關聯, 這樣 data source 就會監聽這個小組件了. 每一次 ui 換 page, data source 就能過去換數據了. 

注意: dataSource.paginator 是一個 getter setter 屬性來的, 所以即使我們在 AfterViewInit 才 set, ui table 一樣會渲染. 

源碼 : 

  get paginator(): MatPaginator | null { return this._paginator; }
  set paginator(paginator: MatPaginator|null) {
    this._paginator = paginator;
    this._updateChangeSubscription(); // ui table 監聽到這個就會 render 了咯
  }
  private _paginator: MatPaginator|null;

 

 

4. Sort 

和上面溝通差不多, 在 table 打上 matSort 指令和 #matSort, 這里要對准 matSort 的 exportAs 哦 

題外話標簽對上指令就要對准 exportAs, 標簽 defualt 是拿 element 和 component 的

  <table mat-table [dataSource]="dataSource" [trackBy]="trackByFn" matSort #matSort="matSort" >

然后我們在我們想 sort 的 header 加上 mat-sort-header 指令

<ng-container matColumnDef="Id">
  <th mat-header-cell *matHeaderCellDef mat-sort-header >Id</th>
  <td mat-cell *matCellDef="let row"> {{row.Id}} </td>
</ng-container>

不是每一個 column 都是可以 sort 的嘛, 當然需要表態一下咯, 

看出來, matSort 基本上只是為了溝通而誕生的.

@ViewChild('matSort', { read: MatSort, static: false })
matSort: MatSort;


ngAfterViewInit() {
  this.dataSource.paginator = this.matPaginator;
  this.dataSource.sort = this.matSort;
}

 

5. filter search 

這個我就不多說了, 

 this.dataSource.filter = 'string here'; 

它也是 setter 所以每次值改變了它都會跑 

 

自定義 data source 

上面說了 material data source 只能處理 local 數據. 但是真實項目里我們的數據大部分是 backend 來的,要通過 api 去拿才行. 

我自己使用了 odata 所以對於做 dynamic table 來說還是比較輕松的. 

我們沿着 material 的思路走, 一樣使用 material 提供的操作小組件 sort, paginator 只是自己實現 data source 就好. 

所以這里我們主要看看怎樣讓它們溝通. 

Paginator 

this.matPaginator.length = 1000;
this.matPaginator.pageIndex = 0; // 注意 0 是開始, 而不是 1
this.matPaginator.page.subscribe((e: PageEvent) => {
  // pageIndex 或 pageSize 變化時觸發
  console.log(e.length);
  console.log(e.pageIndex);
  console.log(e.pageSize);
  console.log(e.previousPageIndex);
});

當 data source 獲取到資料后, 就可以 set length 了 (這個 length 是說數據庫里有多少, 而不是拿下來了多少哦)

如果是 filter update 了, 通常 pageIndex 都會 set to start, 所以這個接口會用到. 

當我們 set pageIndex 和 length 時, 是不會觸發 PageEvent 事件的哦。它會去同步 view 而已.

最后就是監聽用戶的操作了. 

 

 

 

 

 

 

 

 

 

說說目前 material 的缺失. 

1. loading and data not found 

這么基本的功能都沒有.... 

https://github.com/angular/components/issues/8661

workaround : 放 loading 和 not found 放到 table 下面. 沒有 footer 的情況下, 看不出來. 有 footer 自己保重

 

2. column resize 

https://github.com/angular/components/issues/8312

這個也是很基本的功能,也是沒有, 

work aound: 自己可以勉強實現啦... 不過...

 

3. drag and drop row 

這個也是基本功能, 自己實現也是很累的...

https://github.com/angular/components/issues/13770

 

4. visual scroll 

這個不只是 table, 所有 material 能用到 visual scroll 的大部分都沒有 build in 的實現

甚至說,很難去實現... 

https://github.com/angular/components/issues/10122

 

最后吐槽一下, 不只是 material, angular 還有很多很多的功能都不齊全. 

只有用的人才知道它的局限有多大. 當然我的意思不是說其它框架有實現. 

只是作為一個大而全的框架,我對待它的要求就是.... 我只想寫業務邏輯相關的代碼..... 哈哈哈

預計 angular 10 或 11 之后就會很不錯了. 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM