加菲猫火枪手 發表於 2019-6-11 16:57:00

Angular Material (Components Cdk) 学习笔记 Table

<p>refer :&nbsp;</p>
<p>https://material.angular.io/cdk/table/overview</p>
<p>https://material.angular.io/components/table/overview</p>
<p>&nbsp;</p>
<p>通常我们做 control panel 时会大量运用到 table&nbsp;</p>
<p>尤其是处理 CRUD 时, table 更是神器</p>
<p>说到 table 就一定会附带以下这些东西&nbsp;</p>
<p>filter, search&nbsp;</p>
<p>pagination</p>
<p>sort</p>
<p>show/hide/sort columns</p>
<p>select row</p>
<p>cell display&nbsp;</p>
<p>Table 作为高复用组件, 必须要足够抽象。</p>
<p>material 在这里也是一如往常的做出了数据和 ui 分离.&nbsp;</p>
<p>这个概念和 angular directive form 是一样的.&nbsp;</p>
<p>数据方面的处理是交给 DataSource 这个对象来负责.&nbsp;</p>
<p>这个对象监听 filter, search, sort 等等数据的变动,然后对数据进行处理 (mat table data source 目前没有支持远程数据处理, 我们得自己实现)</p>
<p>然后 ui table 通过监听 data source 来获取新数据.&nbsp;</p>
<p>各种小组件, mat-sort, mat-pagination 则是负责监听 ui 操作.</p>
<p>所以是</p>
<p>data source 监听操作小组件 -&gt; 更新 source&nbsp;</p>
<p>ui table 监听 data source -&gt; 渲染</p>
<p>这个就是大致上架构的设计啦,我们只能跟着做了</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>下面说说常用的东西.&nbsp;</p>
<p>1. ui table</p>
<p>首先开一个"家“&nbsp;监听 data source&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">table </span><span style="color: rgba(255, 0, 0, 1)">mat-table </span><span style="color: rgba(0, 0, 255, 1)">="dataSource"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="trackByFn"</span> <span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">table</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>然后是里面</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ng-container </span><span style="color: rgba(255, 0, 0, 1)">matColumnDef</span><span style="color: rgba(0, 0, 255, 1)">="cost"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">th </span><span style="color: rgba(255, 0, 0, 1)">mat-header-cell *matHeaderCellDef</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> Cost <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">th</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">td </span><span style="color: rgba(255, 0, 0, 1)">mat-cell *matCellDef</span><span style="color: rgba(0, 0, 255, 1)">="let data"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> {{data.cost}} <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">td</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">td </span><span style="color: rgba(255, 0, 0, 1)">mat-footer-cell *matFooterCellDef</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> {{totalCost}} <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">td</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">ng-container</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span style="color: rgba(0, 0, 0, 1)">

...

</span><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">tr </span><span style="color: rgba(255, 0, 0, 1)">mat-header-row *matHeaderRowDef</span><span style="color: rgba(0, 0, 255, 1)">="columnsToDisplay"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">tr</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">tr </span><span style="color: rgba(255, 0, 0, 1)">mat-row *matRowDef</span><span style="color: rgba(0, 0, 255, 1)">="let myRowData; columns: columnsToDisplay"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">tr</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">tr </span><span style="color: rgba(255, 0, 0, 1)">mat-footer-row *matFooterRowDef</span><span style="color: rgba(0, 0, 255, 1)">="columnsToDisplay"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">tr</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>定义每一个 column template</p>
<p>一个 column 又分为 3 个 template 展现,&nbsp; header,body,footer</p>
<p>最后 3 行则是定义 row 的呈现.&nbsp;</p>
<p>不是每一个 column 都有 3 个展现的, 有些只有 header, body, 有些则只有 footer, 没有就不要定义就好了</p>
<p>row 的 columnsToDisplay 也不是全部一样的, 有一些 columns 只需要展现在 footer, 那么它就只出现在 row footer 的 columnsToDisplay 就好了。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>2. max-text-column, 这个就是一个方便啦. 因为大部分都是 display string 嘛.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-text-column </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="score"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-text-column</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>源码是很简单的</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Component({
moduleId: module.id,
selector: </span>'mat-text-column'<span style="color: rgba(0, 0, 0, 1)">,
template: `
    </span>&lt;ng-container matColumnDef&gt;
      &lt;th mat-header-cell *matHeaderCellDef ="justify"&gt;<span style="color: rgba(0, 0, 0, 1)">
      {{headerText}}
      </span>&lt;/th&gt;
      &lt;td mat-cell *matCellDef="let data" ="justify"&gt;<span style="color: rgba(0, 0, 0, 1)">
      {{dataAccessor(data, name)}}
      </span>&lt;/td&gt;
    &lt;/ng-container&gt;
<span style="color: rgba(0, 0, 0, 1)">`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.Default,
})
export class MatTextColumn</span>&lt;T&gt; extends CdkTextColumn&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
}</span></pre>
</div>
<p>通过&nbsp;CdkTextColumn&nbsp;@input 去修改 header or cell,&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Input() headerText: string;
@Input() dataAccessor: (data: T, name: string) </span>=&gt; string;</pre>
</div>
<p>&nbsp;</p>
<p>3. Paginator&nbsp;</p>
<p>这个就是上面说的操作小组件啦,&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-paginator </span><span style="color: rgba(255, 0, 0, 1)">#matPaginator </span><span style="color: rgba(0, 0, 255, 1)">=""</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-paginator</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>通过 #matPaginator 打标签, 然后 controller 就可以用 ViewChild 找到它&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@ViewChild('matPaginator', { read: MatPaginator, static: false })
matPaginator: MatPaginator;</span></pre>
</div>
<p>这里我用 static false,大部分情况 true 是 ok 的, 不熟悉的人可以看看 angular 8.0 了解一下.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ngAfterViewInit() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.dataSource.paginator = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.matPaginator;
}</span></pre>
</div>
<p>然后就是让它与 data source 关联, 这样 data source 就会监听这个小组件了. 每一次 ui 换 page, data source 就能过去换数据了.&nbsp;</p>
<p>注意: dataSource.paginator 是一个 getter setter 属性来的, 所以即使我们在 AfterViewInit 才 set, ui table 一样会渲染.&nbsp;</p>
<p>源码 :&nbsp;</p>
<div class="cnblogs_code">
<pre>get paginator(): MatPaginator | <span style="color: rgba(0, 0, 255, 1)">null</span> { <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._paginator; }
set paginator(paginator: MatPaginator</span>|<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>._paginator =<span style="color: rgba(0, 0, 0, 1)"> paginator;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._updateChangeSubscription(); // ui table 监听到这个就会 render 了咯
}
private _paginator: MatPaginator</span>|<span style="color: rgba(0, 0, 255, 1)">null</span>;</pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>4. Sort&nbsp;</p>
<p>和上面沟通差不多, 在 table 打上 matSort 指令和 #matSort, 这里要对准 matSort 的 exportAs 哦&nbsp;</p>
<p>题外话标签对上指令就要对准 exportAs, 标签 defualt 是拿 element 和 component 的</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">table </span><span style="color: rgba(255, 0, 0, 1)">mat-table </span><span style="color: rgba(0, 0, 255, 1)">="dataSource"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="trackByFn"</span><span style="color: rgba(255, 0, 0, 1)"> matSort #matSort</span><span style="color: rgba(0, 0, 255, 1)">="matSort"</span> <span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>然后我们在我们想 sort 的 header 加上 mat-sort-header 指令</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ng-container </span><span style="color: rgba(255, 0, 0, 1)">matColumnDef</span><span style="color: rgba(0, 0, 255, 1)">="Id"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">th </span><span style="color: rgba(255, 0, 0, 1)">mat-header-cell *matHeaderCellDef mat-sort-header </span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>Id<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">th</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">td </span><span style="color: rgba(255, 0, 0, 1)">mat-cell *matCellDef</span><span style="color: rgba(0, 0, 255, 1)">="let row"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> {{row.Id}} <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">td</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">ng-container</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>不是每一个 column 都是可以 sort 的嘛, 当然需要表态一下咯,&nbsp;</p>
<p>看出来, matSort 基本上只是为了沟通而诞生的.</p>
<div class="cnblogs_code">
<pre>@ViewChild('matSort', { read: MatSort, static: <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> })
matSort: MatSort;


ngAfterViewInit() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.dataSource.paginator = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.matPaginator;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.dataSource.sort = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.matSort;
}</span></pre>
</div>
<p>&nbsp;</p>
<p>5. filter search&nbsp;</p>
<p>这个我就不多说了,&nbsp;</p>
<p>&nbsp;this.dataSource.filter = 'string here';&nbsp;</p>
<p>它也是 setter 所以每次值改变了它都会跑&nbsp;</p>
<p>&nbsp;</p>
<p>自定义 data source&nbsp;</p>
<p>上面说了 material data source 只能处理 local 数据. 但是真实项目里我们的数据大部分是 backend 来的,要通过 api 去拿才行.&nbsp;</p>
<p>我自己使用了 odata 所以对于做 dynamic table 来说还是比较轻松的.&nbsp;</p>
<p>我们沿着 material 的思路走, 一样使用 material 提供的操作小组件 sort, paginator 只是自己实现 data source 就好.&nbsp;</p>
<p>所以这里我们主要看看怎样让它们沟通.&nbsp;</p>
<p>Paginator&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">this</span>.matPaginator.length = 1000<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.matPaginator.pageIndex = 0; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注意 0 是开始, 而不是 1</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.matPaginator.page.subscribe((e: PageEvent) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pageIndex 或 pageSize 变化时触发</span>
<span style="color: rgba(0, 0, 0, 1)">console.log(e.length);
console.log(e.pageIndex);
console.log(e.pageSize);
console.log(e.previousPageIndex);
});</span></pre>
</div>
<p>当 data source 获取到资料后, 就可以 set length 了 (这个 length 是说数据库里有多少, 而不是拿下来了多少哦)</p>
<p>如果是 filter update 了, 通常 pageIndex 都会 set to start, 所以这个接口会用到.&nbsp;</p>
<p>当我们 set pageIndex 和 length 时, 是不会触发 PageEvent 事件的哦。它会去同步 view 而已.</p>
<p>最后就是监听用户的操作了.&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>说说目前 material 的缺失.&nbsp;</p>
<p>1. loading and data not found&nbsp;</p>
<p>这么基本的功能都没有....&nbsp;</p>
<p>https://github.com/angular/components/issues/8661</p>
<p>workaround : 放 loading 和 not found 放到 table 下面. 没有 footer 的情况下, 看不出来. 有 footer 自己保重</p>
<p>&nbsp;</p>
<p>2. column resize&nbsp;</p>
<p>https://github.com/angular/components/issues/8312</p>
<p>这个也是很基本的功能,也是没有,&nbsp;</p>
<p>work aound: 自己可以勉强实现啦... 不过...</p>
<p>&nbsp;</p>
<p>3. drag and drop row&nbsp;</p>
<p>这个也是基本功能, 自己实现也是很累的...</p>
<p>https://github.com/angular/components/issues/13770</p>
<p>&nbsp;</p>
<p>4. visual scroll&nbsp;</p>
<p>这个不只是 table, 所有 material 能用到 visual scroll 的大部分都没有 build in 的实现</p>
<p>甚至说,很难去实现...&nbsp;</p>
<p>https://github.com/angular/components/issues/10122</p>
<p>&nbsp;</p>
<p>最后吐槽一下, 不只是 material, angular 还有很多很多的功能都不齐全.&nbsp;</p>
<p>只有用的人才知道它的局限有多大. 当然我的意思不是说其它框架有实现.&nbsp;</p>
<p>只是作为一个大而全的框架,我对待它的要求就是.... 我只想写业务逻辑相关的代码..... 哈哈哈</p>
<p>预计 angular 10 或 11 之后就会很不错了.&nbsp;</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/keatkeat/p/11004658.html
頁: [1]
查看完整版本: Angular Material (Components Cdk) 学习笔记 Table