表格设置
表格是业务系统的基石.
我们收集了一些项目上使用 Ng Zorro 表格的反馈意见.
-
当表格列较多时,如果不进行宽度设置, 则会显示得很畸形.
要解决这个问题, 需要设置表格 nzScroll 属性的 x 值.
nzScroll 的 x 可以让表格产生横向的滚动条, 从而将表格内容拉伸.
不过这个值应该设置成多少合适, 则是一门学问.
通常需要计算表格中有多少列,每列大致占多少宽度, nzScroll.x 的值大致是这些宽度之和.
手工计算宽度费时费力, 最好是能自动计算.
-
另一个问题是冻结表格头, 并让表格在一定高度滚动.
通过设置 nzScroll 属性的 y 值可以做到这一点.
不过设置 nzScroll.y 也是一门学问, 因为不同屏幕大小可能需要设置不同的值,在开发阶段很难固定.
一些公司使用某些方法计算以达到自适应高度,不过大多针对比较固定的页面布局,且相对简单.
更好的办法是让用户在运行时根据自己的要求动态更新.
-
除了表格的总宽度, 每个列的宽度设置也是一个头痛的问题.
列宽大多与内容相关, 在开发阶段设置固定列宽, 当内容超过固定宽度就会出现换行,影响美观.
如果在开发阶段设置一个默认宽度, 并在运行时可由用户修改就能解决问题.
当然最好能支持拖动表头修改列宽, 则更为方便.
-
自定义列是很多项目的必备功能.
当表格列非常多, 用户希望只显示其中感兴趣的一部分列, 并能修改列的显示顺序.
Ng Zorro 支持自定义列功能, 不过使用起来比较复杂.
当你启用了自定义列, 用来固定左右侧的 nzLeft 和 nzRight 就变得不那么利索.
列与列之间经常会出现一些缝隙或对不齐的现象, Ng Zorro 官方文档给出了一些调整建议, 不过也是非常麻烦.
-
诸如表格批量编辑,表格行编辑, 树形异步加载等需求都是很早之前就已经扩展支持, 就不在此一一列出.
下面介绍 Util UI 表格设置功能.
先来一个表格设置的效果图.

可以看到, 它确实解决了前面提到的棘手问题.
如何开启表格设置功能?
表格标签示例代码.
@*表格*@
<util-table id="tb" key="identity_operation" enable-table-settings="true"
show-checkbox="true" show-line-number="true"
url="operation" query-param="queryParam" sort="SortId">
<util-td for="Name"></util-td>
<util-td for="Uri"></util-td>
<util-td for="IsBase" sort="false"></util-td>
<util-td for="Remark"></util-td>
<util-td for="Enabled">
<util-tag color-type="GeekBlue" ng-if="row.enabled" text-enabled="true"></util-tag>
<util-tag color-type="Red" ng-if="!row.enabled" text-not-enabled="true"></util-tag>
</util-td>
<util-td for="CreationTime"></util-td>
<util-td for="LastModificationTime"></util-td>
<util-td title-operation="true">
<util-a on-click="openDetailDialog(row)" text-detail="true"></util-a>
<util-container acl="operation.update">
<util-divider type="Vertical"></util-divider>
<util-a on-click="openEditDrawer(row)" text-update="true"></util-a>
</util-container>
<util-container acl="operation.delete">
<util-divider type="Vertical"></util-divider>
<util-a danger="true" on-click="delete(row.id)" text-delete="true"></util-a>
</util-container>
</util-td>
</util-table>
要开启表格设置功能, 只需要在 <util-table> 标签设置 enable-table-settings 属性为 true.
你可能要问, 需要编写 ts 脚本代码吗?
不用 !!!
如果你看过 Ng Zorro 官方自定义列的示例, 知道需要将一个 NzCustomColumn[] 对象传入 <nz-table>的 nzCustomColumn 属性.
那么, Util UI 的自定义列功能是否使用 Ng Zorro 官方的实现呢?
下面来看看生成的 html , 答案就会揭晓.
<nz-table #tb="" #x_tb="xTableExtend" (nzPageIndexChange)="x_tb.pageIndexChange($event)"
(nzPageSizeChange)="x_tb.pageSizeChange($event)" order="SortId" url="operation" x-table-extend=""
[(nzPageIndex)]="x_tb.queryParam.page" [(nzPageSize)]="x_tb.queryParam.pageSize" [(queryParam)]="queryParam"
[nzBordered]="ts_tb.bordered" [nzCustomColumn]="ts_tb.columns" [nzData]="x_tb.dataSource"
[nzFrontPagination]="false" [nzLoading]="x_tb.loading" [nzPageSizeOptions]="x_tb.pageSizeOptions"
[nzScroll]="ts_tb.scroll" [nzShowQuickJumper]="true" [nzShowSizeChanger]="true" [nzShowTotal]="total_tb"
[nzSize]="ts_tb.size" [nzTotal]="x_tb.total">
<thead>
<tr>
<th (nzCheckedChange)="x_tb.masterToggle()" nzCellControl="util.checkbox"
[nzChecked]="x_tb.isMasterChecked()" [nzDisabled]="!x_tb.dataSource.length"
[nzIndeterminate]="x_tb.isMasterIndeterminate()" [nzLeft]="ts_tb.isLeft('util.checkbox')"
[nzRight]="ts_tb.isRight('util.checkbox')" [nzShowCheckbox]="true"
[titleAlign]="ts_tb.getTitleAlign('util.checkbox')">
</th>
<th nzCellControl="util.lineNumber" [nzLeft]="ts_tb.isLeft('util.lineNumber')"
[nzRight]="ts_tb.isRight('util.lineNumber')" [titleAlign]="ts_tb.getTitleAlign('util.lineNumber')">
{{'util.lineNumber'|i18n}}
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'identity.operation.name')"
(nzSortOrderChange)="x_tb.sortChange('name',$event)" nz-resizable="" nzBounds="window"
nzCellControl="identity.operation.name" nzPreview="" [nzLeft]="ts_tb.isLeft('identity.operation.name')"
[nzRight]="ts_tb.isRight('identity.operation.name')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('identity.operation.name')">
{{'identity.operation.name'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'identity.operation.uri')"
(nzSortOrderChange)="x_tb.sortChange('uri',$event)" nz-resizable="" nzBounds="window"
nzCellControl="identity.operation.uri" nzPreview="" [nzLeft]="ts_tb.isLeft('identity.operation.uri')"
[nzRight]="ts_tb.isRight('identity.operation.uri')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('identity.operation.uri')">
{{'identity.operation.uri'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'identity.operation.isBase')" nz-resizable="" nzBounds="window"
nzCellControl="identity.operation.isBase" nzPreview=""
[nzLeft]="ts_tb.isLeft('identity.operation.isBase')"
[nzRight]="ts_tb.isRight('identity.operation.isBase')"
[titleAlign]="ts_tb.getTitleAlign('identity.operation.isBase')">
{{'identity.operation.isBase'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'identity.operation.remark')"
(nzSortOrderChange)="x_tb.sortChange('remark',$event)" nz-resizable="" nzBounds="window"
nzCellControl="identity.operation.remark" nzPreview=""
[nzLeft]="ts_tb.isLeft('identity.operation.remark')"
[nzRight]="ts_tb.isRight('identity.operation.remark')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('identity.operation.remark')">
{{'identity.operation.remark'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'identity.operation.enabled')"
(nzSortOrderChange)="x_tb.sortChange('enabled',$event)" nz-resizable="" nzBounds="window"
nzCellControl="identity.operation.enabled" nzPreview=""
[nzLeft]="ts_tb.isLeft('identity.operation.enabled')"
[nzRight]="ts_tb.isRight('identity.operation.enabled')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('identity.operation.enabled')">
{{'identity.operation.enabled'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'util.creationTime')"
(nzSortOrderChange)="x_tb.sortChange('creationTime',$event)" nz-resizable="" nzBounds="window"
nzCellControl="util.creationTime" nzPreview="" [nzLeft]="ts_tb.isLeft('util.creationTime')"
[nzRight]="ts_tb.isRight('util.creationTime')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('util.creationTime')">{{'util.creationTime'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'util.lastModificationTime')"
(nzSortOrderChange)="x_tb.sortChange('lastModificationTime',$event)" nz-resizable="" nzBounds="window"
nzCellControl="util.lastModificationTime" nzPreview=""
[nzLeft]="ts_tb.isLeft('util.lastModificationTime')"
[nzRight]="ts_tb.isRight('util.lastModificationTime')" [nzShowSort]="true" [nzSortFn]="true"
[titleAlign]="ts_tb.getTitleAlign('util.lastModificationTime')">
{{'util.lastModificationTime'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
<th (nzResizeEnd)="ts_tb.handleResize($event,'util.operation')" nz-resizable="" nzBounds="window"
nzCellControl="util.operation" nzPreview="" [nzLeft]="ts_tb.isLeft('util.operation')"
[nzRight]="ts_tb.isRight('util.operation')" [titleAlign]="ts_tb.getTitleAlign('util.operation')">
{{'util.operation'|i18n}}
<nz-resize-handle nzDirection="right"></nz-resize-handle>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of x_tb.dataSource;index as index">
<td (click)="$event.stopPropagation()" (nzCheckedChange)="x_tb.toggle(row)" nzCellControl="util.checkbox"
[nzAlign]="ts_tb.getAlign('util.checkbox')" [nzChecked]="x_tb.isChecked(row)"
[nzLeft]="ts_tb.isLeft('util.checkbox')" [nzRight]="ts_tb.isRight('util.checkbox')"
[nzShowCheckbox]="true">
</td>
<td nzCellControl="util.lineNumber" [nzAlign]="ts_tb.getAlign('util.lineNumber')"
[nzLeft]="ts_tb.isLeft('util.lineNumber')" [nzRight]="ts_tb.isRight('util.lineNumber')">
{{row.lineNumber}}
</td>
<td nzCellControl="identity.operation.name" [nzAlign]="ts_tb.getAlign('identity.operation.name')"
[nzEllipsis]="ts_tb.getEllipsis('identity.operation.name')"
[nzLeft]="ts_tb.isLeft('identity.operation.name')" [nzRight]="ts_tb.isRight('identity.operation.name')">
{{row.name}}
</td>
<td nzCellControl="identity.operation.uri" [nzAlign]="ts_tb.getAlign('identity.operation.uri')"
[nzEllipsis]="ts_tb.getEllipsis('identity.operation.uri')"
[nzLeft]="ts_tb.isLeft('identity.operation.uri')" [nzRight]="ts_tb.isRight('identity.operation.uri')">
{{row.uri}}
</td>
<td nzCellControl="identity.operation.isBase" [nzAlign]="ts_tb.getAlign('identity.operation.isBase')"
[nzEllipsis]="ts_tb.getEllipsis('identity.operation.isBase')"
[nzLeft]="ts_tb.isLeft('identity.operation.isBase')"
[nzRight]="ts_tb.isRight('identity.operation.isBase')">
<i *ngIf="!row.isBase" nz-icon nzType="close"></i>
<i *ngIf="row.isBase" nz-icon nzType="check"></i>
</td>
<td nzCellControl="identity.operation.remark" [nzAlign]="ts_tb.getAlign('identity.operation.remark')"
[nzEllipsis]="ts_tb.getEllipsis('identity.operation.remark')"
[nzLeft]="ts_tb.isLeft('identity.operation.remark')"
[nzRight]="ts_tb.isRight('identity.operation.remark')">
{{row.remark}}
</td>
<td nzCellControl="identity.operation.enabled" [nzAlign]="ts_tb.getAlign('identity.operation.enabled')"
[nzEllipsis]="ts_tb.getEllipsis('identity.operation.enabled')"
[nzLeft]="ts_tb.isLeft('identity.operation.enabled')"
[nzRight]="ts_tb.isRight('identity.operation.enabled')">
<nz-tag *ngIf="row.enabled" nzColor="geekblue">{{'util.enabled'|i18n}}</nz-tag>
<nz-tag *ngIf="!row.enabled" nzColor="red">{{'util.notEnabled'|i18n}}</nz-tag>
</td>
<td nzCellControl="util.creationTime" [nzAlign]="ts_tb.getAlign('util.creationTime')"
[nzEllipsis]="ts_tb.getEllipsis('util.creationTime')" [nzLeft]="ts_tb.isLeft('util.creationTime')"
[nzRight]="ts_tb.isRight('util.creationTime')">
{{row.creationTime|date:'yyyy-MM-dd HH:mm'}}
</td>
<td nzCellControl="util.lastModificationTime" [nzAlign]="ts_tb.getAlign('util.lastModificationTime')"
[nzEllipsis]="ts_tb.getEllipsis('util.lastModificationTime')"
[nzLeft]="ts_tb.isLeft('util.lastModificationTime')"
[nzRight]="ts_tb.isRight('util.lastModificationTime')">
{{row.lastModificationTime|date:'yyyy-MM-dd HH:mm'}}
</td>
<td nzCellControl="util.operation" [nzAlign]="ts_tb.getAlign('util.operation')"
[nzEllipsis]="ts_tb.getEllipsis('util.operation')" [nzLeft]="ts_tb.isLeft('util.operation')"
[nzRight]="ts_tb.isRight('util.operation')">
<a (click)="openDetailDialog(row)">{{'util.detail'|i18n}}</a>
<ng-container *aclIf="'operation.update'">
<nz-divider nzType="vertical"></nz-divider>
<a (click)="openEditDrawer(row)">{{'util.update'|i18n}}</a>
</ng-container>
<ng-container *aclIf="'operation.delete'">
<nz-divider nzType="vertical"></nz-divider>
<a (click)="delete(row.id)" class="ant-btn-dangerous">{{'util.delete'|i18n}}</a>
</ng-container>
</td>
</tr>
</tbody>
</nz-table>
<ng-template #total_tb="" let-range="range" let-total="">
{{ 'util.tableTotalTemplate'|i18n:{start:range[0],end:range[1],total:total} }}
</ng-template>
<x-table-settings #ts_tb=""
key="identity_operation" [enableFixedColumn]="true"
[initColumns]="[{'title':'util.checkbox','width':x_tb.config.table.checkboxWidth,'align':'left'},
{'title':'util.lineNumber','width':x_tb.config.table.lineNumberWidth,'align':'left'},
{'title':'identity.operation.name'},{'title':'identity.operation.uri'},
{'title':'identity.operation.isBase'},{'title':'identity.operation.remark'},
{'title':'identity.operation.enabled'},{'title':'util.creationTime'},
{'title':'util.lastModificationTime'},{'title':'util.operation'}]">
</x-table-settings>
观察 <nz-table> 标签, 可以发现 [nzCustomColumn]="ts_tb.columns" , 说明确实使用的是 Ng Zorro 官方提供的自定义列功能.
生成的 html 比较复杂, enable-table-settings 除了开启自定义列外,还会启用拖动列宽等功能.
前面提到, Util Ui 提供的标签可以压缩 3-10 倍的 html 代码量 , 从这里可以看出, 绝非信口雌黄.
<x-table-settings> 是由 util-angular 脚本库提供的表格设置组件.
<x-table-settings> 的 initColumns 属性设置了一个列信息数组, 将列集合传入表格设置组件.
<x-table-settings> 组件经过系列工序, 输出 Ng Zorro 需要的自定义列信息.
所以, 无需手工编写任何 ts 脚本代码, 即可完成相关功能.
可以看到, TagHelper 不仅可以封装 html 复杂度,甚至能为你生成一些简单的 js 对象.
要打开表格设置对话框, 需要一个按钮.
.cshtml 代码如下.
show-table-settings 用于显示表格设置对话框, 传入表格的引用变量名 tb.
<util-a show-table-settings="tb"></util-a>
生成的 html 如下.
<a (click)="ts_tb.show()" nz-tooltip="" [nzTooltipTitle]="'util.tableSettings'|i18n">
<i nz-icon="" nzType="setting"></i>
</a>
Util UI 的扩展指令和组件具有一些约定的命名.
表格组件的引用变量名为 tb , 对应的表格设置组件则为 ts_tb .
表格设置组件提供了一个 show() 函数, 调用该函数即可打开表格设置窗口.