- Table表格
- 何时使用
- 如何使用
- 高度定制
- 组件增强
- 数据处理
- 代码演示
- API
- 单独引入此组件
- nz-tablecomponent
- th
- td
- thead
- tr
- [nz-virtual-scroll]directive
- 注意
Table表格
展示行列数据。
何时使用
- 当有大量结构化的数据需要展现时;
- 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。
如何使用
Table 组件同时具备了易用性和高度可定制性
高度定制
在 nz-table
组件中完整的暴露了 W3C标准 <table>
的所有组成部分,你可以像使用 table
元素一样使用 nz-table
,根据依据业务需求,使用者可以自由的控制任何一个部分的样式、内容、逻辑和事件绑定。
组件增强
在 nz-table
, thead
, th
, td
等多个暴露的元素上,组件提供了增强语法,经过配置之后可以很方便的实现多选、过滤、排序、固定列、固定表头、服务端分页等功能。
数据处理
传入[nzData]
中的数据,经过处理之后,可以通过 模板变量 获取当前展示表格部分的数据,再使用 *ngFor
依据需求将数据渲染。
<nz-table #basicTable [nzData]="dataSet">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{data.name}}</td>
<td>{{data.age}}</td>
<td>{{data.address}}</td>
<td>
<a>Action 一 {{data.name}}</a>
<nz-divider nzType="vertical"></nz-divider>
<a>Delete</a>
</td>
</tr>
</tbody>
</nz-table>
代码演示
基本用法
简单的表格,最后一列是各种操作。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-basic',
template: `
<nz-table #basicTable [nzData]="listOfData">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
<td>
<a>Action 一 {{ data.name }}</a>
<nz-divider nzType="vertical"></nz-divider>
<a>Delete</a>
</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableBasicComponent {
listOfData = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
}
选择和操作
第一列是联动的选择框,增加 nzShowCheckbox
后,th
获得和 nz-checkbox
一样的功能,选择后进行操作,完成后清空选择,请注意:数据逻辑需要自行控制。
import { Component, OnInit } from '@angular/core';
export interface Data {
id: number;
name: string;
age: number;
address: string;
disabled: boolean;
}
@Component({
selector: 'nz-demo-table-row-selection-and-operation',
template: `
<div class="operate">
<button
nz-button
[disabled]="numberOfChecked === 0"
[nzType]="'primary'"
[nzLoading]="isOperating"
(click)="operateData()"
>
Reload
</button>
<span *ngIf="numberOfChecked">Selected {{ numberOfChecked }} items</span>
</div>
<nz-table
#rowSelectionTable
nzShowPagination
nzShowSizeChanger
[nzData]="listOfAllData"
(nzCurrentPageDataChange)="currentPageDataChange($event)"
>
<thead>
<tr>
<th
nzShowCheckbox
[(nzChecked)]="isAllDisplayDataChecked"
[nzIndeterminate]="isIndeterminate"
(nzCheckedChange)="checkAll($event)"
></th>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of rowSelectionTable.data">
<td
nzShowCheckbox
[(nzChecked)]="mapOfCheckedId[data.id]"
[nzDisabled]="data.disabled"
(nzCheckedChange)="refreshStatus()"
></td>
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
.operate {
margin-bottom: 16px;
}
.operate span {
margin-left: 8px;
}
`
]
})
export class NzDemoTableRowSelectionAndOperationComponent implements OnInit {
isAllDisplayDataChecked = false;
isOperating = false;
isIndeterminate = false;
listOfDisplayData: Data[] = [];
listOfAllData: Data[] = [];
mapOfCheckedId: { [key: string]: boolean } = {};
numberOfChecked = 0;
currentPageDataChange($event: Data[]): void {
this.listOfDisplayData = $event;
this.refreshStatus();
}
refreshStatus(): void {
this.isAllDisplayDataChecked = this.listOfDisplayData
.filter(item => !item.disabled)
.every(item => this.mapOfCheckedId[item.id]);
this.isIndeterminate =
this.listOfDisplayData.filter(item => !item.disabled).some(item => this.mapOfCheckedId[item.id]) &&
!this.isAllDisplayDataChecked;
this.numberOfChecked = this.listOfAllData.filter(item => this.mapOfCheckedId[item.id]).length;
}
checkAll(value: boolean): void {
this.listOfDisplayData.filter(item => !item.disabled).forEach(item => (this.mapOfCheckedId[item.id] = value));
this.refreshStatus();
}
operateData(): void {
this.isOperating = true;
setTimeout(() => {
this.listOfAllData.forEach(item => (this.mapOfCheckedId[item.id] = false));
this.refreshStatus();
this.isOperating = false;
}, 1000);
}
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfAllData.push({
id: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
disabled: i % 2 === 0
});
}
}
}
自定义选择项
通过 nzShowRowSelection
与 nzSelections
自定义选择项.
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-row-selection-custom',
template: `
<nz-table
#rowSelectionTable
nzShowSizeChanger
[nzData]="listOfAllData"
(nzCurrentPageDataChange)="currentPageDataChange($event)"
>
<thead>
<tr>
<th
nzShowCheckbox
nzShowRowSelection
[nzSelections]="listOfSelection"
[(nzChecked)]="isAllDisplayDataChecked"
[nzIndeterminate]="isIndeterminate"
(nzCheckedChange)="checkAll($event)"
></th>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of rowSelectionTable.data">
<td nzShowCheckbox [(nzChecked)]="mapOfCheckedId[data.id]" (nzCheckedChange)="refreshStatus()"></td>
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableRowSelectionCustomComponent implements OnInit {
listOfSelection = [
{
text: 'Select All Row',
onSelect: () => {
this.checkAll(true);
}
},
{
text: 'Select Odd Row',
onSelect: () => {
this.listOfDisplayData.forEach((data, index) => (this.mapOfCheckedId[data.id] = index % 2 !== 0));
this.refreshStatus();
}
},
{
text: 'Select Even Row',
onSelect: () => {
this.listOfDisplayData.forEach((data, index) => (this.mapOfCheckedId[data.id] = index % 2 === 0));
this.refreshStatus();
}
}
];
isAllDisplayDataChecked = false;
isIndeterminate = false;
listOfDisplayData: any[] = [];
listOfAllData: any[] = [];
mapOfCheckedId: { [key: string]: boolean } = {};
currentPageDataChange($event: Array<{ id: number; name: string; age: number; address: string }>): void {
this.listOfDisplayData = $event;
this.refreshStatus();
}
refreshStatus(): void {
this.isAllDisplayDataChecked = this.listOfDisplayData.every(item => this.mapOfCheckedId[item.id]);
this.isIndeterminate =
this.listOfDisplayData.some(item => this.mapOfCheckedId[item.id]) && !this.isAllDisplayDataChecked;
}
checkAll(value: boolean): void {
this.listOfDisplayData.forEach(item => (this.mapOfCheckedId[item.id] = value));
this.refreshStatus();
}
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfAllData.push({
id: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`
});
}
}
}
筛选和排序
对某一列数据进行筛选,通过指定 th
的 nzShowFilter
属性来展示筛选菜单, 使用 nzFilters
属性来指定筛选选项,nzFilterChange
用于获取当前选中的选项,nzFilterMultiple
用于指定多选和单选。
对某一列数据进行排序,通过指定 th
的 nzShowSort
属性来展示排序按钮,使用 nzSortKey
来指定排序的 key,在 thead
上通过 nzSortChange
来获取排序改变事件,通过 nzSingleSort
来指定是否单列排序。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-head',
template: `
<nz-table #filterTable [nzData]="listOfDisplayData">
<thead (nzSortChange)="sort($event)" nzSingleSort>
<tr>
<th
nzShowSort
nzSortKey="name"
nzShowFilter
[nzFilters]="listOfName"
(nzFilterChange)="filter($event, searchAddress)"
>
Name
</th>
<th nzShowSort nzSortKey="age">Age</th>
<th
nzShowSort
nzSortKey="address"
nzShowFilter
[nzFilterMultiple]="false"
[nzFilters]="listOfAddress"
(nzFilterChange)="filter(listOfSearchName, $event)"
>
Address
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of filterTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableHeadComponent {
sortName: string | null = null;
sortValue: string | null = null;
searchAddress: string;
listOfName = [{ text: 'Joe', value: 'Joe' }, { text: 'Jim', value: 'Jim' }];
listOfAddress = [{ text: 'London', value: 'London' }, { text: 'Sidney', value: 'Sidney' }];
listOfSearchName: string[] = [];
listOfData: Array<{ name: string; age: number; address: string; [key: string]: string | number }> = [
{
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
},
{
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park'
}
];
listOfDisplayData: Array<{ name: string; age: number; address: string; [key: string]: string | number }> = [
...this.listOfData
];
sort(sort: { key: string; value: string }): void {
this.sortName = sort.key;
this.sortValue = sort.value;
this.search();
}
filter(listOfSearchName: string[], searchAddress: string): void {
this.listOfSearchName = listOfSearchName;
this.searchAddress = searchAddress;
this.search();
}
search(): void {
/** filter data **/
const filterFunc = (item: { name: string; age: number; address: string }) =>
(this.searchAddress ? item.address.indexOf(this.searchAddress) !== -1 : true) &&
(this.listOfSearchName.length ? this.listOfSearchName.some(name => item.name.indexOf(name) !== -1) : true);
const data = this.listOfData.filter(item => filterFunc(item));
/** sort data **/
if (this.sortName && this.sortValue) {
this.listOfDisplayData = data.sort((a, b) =>
this.sortValue === 'ascend'
? a[this.sortName!] > b[this.sortName!]
? 1
: -1
: b[this.sortName!] > a[this.sortName!]
? 1
: -1
);
} else {
this.listOfDisplayData = data;
}
}
}
默认筛选
通过设置 filter 对象的 { byDefault: true }
属性来默认启用一个筛选器。注意,你必须同时自行设置过滤后应当展示的列表项,为了保持数据流的清晰和数据的一致性,组件库不会为你做这项工作。详情请见 demo。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-default-filter',
template: `
<nz-table #filterTable [nzData]="listOfDisplayData">
<thead (nzSortChange)="sort($event)" nzSingleSort>
<tr>
<th
nzShowSort
nzSortKey="name"
nzShowFilter
[nzFilters]="listOfName"
(nzFilterChange)="filter($event, searchAddress)"
>
Name
</th>
<th nzShowSort nzSortKey="age">Age</th>
<th
nzShowSort
nzSortKey="address"
nzShowFilter
[nzFilterMultiple]="false"
[nzFilters]="listOfAddress"
(nzFilterChange)="filter(listOfSearchName, $event)"
>
Address
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of filterTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableDefaultFilterComponent {
listOfName = [{ text: 'Joe', value: 'Joe', byDefault: true }, { text: 'Jim', value: 'Jim' }];
listOfAddress = [{ text: 'London', value: 'London', byDefault: true }, { text: 'Sidney', value: 'Sidney' }];
listOfSearchName = ['Joe']; // You need to change it as well!
sortName: string | null = null;
sortValue: string | null = null;
searchAddress = 'London';
listOfData: Array<{ name: string; age: number; address: string; [key: string]: string | number }> = [
{
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
},
{
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park'
}
];
// You need to change it as well!
listOfDisplayData: Array<{ name: string; age: number; address: string; [key: string]: string | number }> = [];
sort(sort: { key: string; value: string }): void {
this.sortName = sort.key;
this.sortValue = sort.value;
this.search();
}
filter(listOfSearchName: string[], searchAddress: string): void {
console.log(listOfSearchName, searchAddress);
this.listOfSearchName = listOfSearchName;
this.searchAddress = searchAddress;
this.search();
}
search(): void {
/** filter data **/
const filterFunc = (item: { name: string; age: number; address: string }) =>
(this.searchAddress ? item.address.indexOf(this.searchAddress) !== -1 : true) &&
(this.listOfSearchName.length ? this.listOfSearchName.some(name => item.name.indexOf(name) !== -1) : true);
const data = this.listOfData.filter(item => filterFunc(item));
/** sort data **/
if (this.sortName && this.sortValue) {
this.listOfDisplayData = data.sort((a, b) =>
this.sortValue === 'ascend'
? a[this.sortName!] > b[this.sortName!]
? 1
: -1
: b[this.sortName!] > a[this.sortName!]
? 1
: -1
);
} else {
this.listOfDisplayData = data;
}
}
}
可控的筛选和排序
使用受控属性对筛选状态进行控制。
- 在
th
中定义了nzSort
属性即视为受控模式。- 通过手动指定
nzSort
来指定当前列的排序状态- 通过
th
的nzSortChange
事件来获取当前列排序状态的改变- 不可与
thead
中的nzSortChange
或nzSingleSort
同时使用
import { Component } from '@angular/core';
interface Data {
name: string;
age: number;
address: string;
[key: string]: any;
}
@Component({
selector: 'nz-demo-table-reset-filter',
template: `
<div class="table-operations">
<button nz-button (click)="sort('age', 'descend')">Sort age</button>
<button nz-button (click)="resetFilters()">Clear filters</button>
<button nz-button (click)="resetSortAndFilters()">Clear filters and sorters</button>
</div>
<nz-table #filterTable [nzData]="listOfDisplayData">
<thead>
<tr>
<th
nzShowSort
nzShowFilter
[(nzSort)]="mapOfSort.name"
(nzSortChange)="sort('name', $event)"
[nzFilters]="listOfFilterName"
(nzFilterChange)="search($event, listOfSearchAddress)"
>
Name
</th>
<th nzShowSort [(nzSort)]="mapOfSort.age" (nzSortChange)="sort('age', $event)">Age</th>
<th
nzShowSort
nzShowFilter
[(nzSort)]="mapOfSort.address"
(nzSortChange)="sort('address', $event)"
[nzFilters]="listOfFilterAddress"
(nzFilterChange)="search(listOfSearchName, $event)"
>
Address
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of filterTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
.table-operations {
margin-bottom: 16px;
}
.table-operations > button {
margin-right: 8px;
}
`
]
})
export class NzDemoTableResetFilterComponent {
listOfSearchName: string[] = [];
listOfSearchAddress: string[] = [];
listOfFilterName = [{ text: 'Joe', value: 'Joe' }, { text: 'Jim', value: 'Jim' }];
listOfFilterAddress = [{ text: 'London', value: 'London' }, { text: 'Sidney', value: 'Sidney' }];
listOfData: Data[] = [
{
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
},
{
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park'
}
];
listOfDisplayData = [...this.listOfData];
mapOfSort: { [key: string]: any } = {
name: null,
age: null,
address: null
};
sortName: string | null = null;
sortValue: string | null = null;
sort(sortName: string, value: string): void {
this.sortName = sortName;
this.sortValue = value;
for (const key in this.mapOfSort) {
this.mapOfSort[key] = key === sortName ? value : null;
}
this.search(this.listOfSearchName, this.listOfSearchAddress);
}
search(listOfSearchName: string[], listOfSearchAddress: string[]): void {
this.listOfSearchName = listOfSearchName;
this.listOfSearchAddress = listOfSearchAddress;
const filterFunc = (item: Data) =>
(this.listOfSearchAddress.length
? this.listOfSearchAddress.some(address => item.address.indexOf(address) !== -1)
: true) &&
(this.listOfSearchName.length ? this.listOfSearchName.some(name => item.name.indexOf(name) !== -1) : true);
const listOfData = this.listOfData.filter((item: Data) => filterFunc(item));
if (this.sortName && this.sortValue) {
this.listOfDisplayData = listOfData.sort((a, b) =>
this.sortValue === 'ascend'
? a[this.sortName!] > b[this.sortName!]
? 1
: -1
: b[this.sortName!] > a[this.sortName!]
? 1
: -1
);
} else {
this.listOfDisplayData = listOfData;
}
}
resetFilters(): void {
this.listOfFilterName = [{ text: 'Joe', value: 'Joe' }, { text: 'Jim', value: 'Jim' }];
this.listOfFilterAddress = [{ text: 'London', value: 'London' }, { text: 'Sidney', value: 'Sidney' }];
this.listOfSearchName = [];
this.listOfSearchAddress = [];
this.search(this.listOfSearchName, this.listOfSearchAddress);
}
resetSortAndFilters(): void {
this.sortName = null;
this.sortValue = null;
this.mapOfSort = {
name: null,
age: null,
address: null
};
this.resetFilters();
this.search(this.listOfSearchName, this.listOfSearchAddress);
}
}
自定义筛选菜单
通过 nz-dropdown
、nzFilters
和 nzFilterChange
定义自定义的列筛选功能,并实现一个搜索列的示例,实际使用中建议将搜索组件进行单独封装。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-custom-filter-panel',
template: `
<nz-table #nzTable [nzData]="listOfDisplayData">
<thead>
<tr>
<th nzCustomFilter>
Name
<nz-dropdown nzTrigger="click" nzPlacement="bottomRight" [nzClickHide]="false" nzTableFilter #dropdown>
<i
nz-icon
nzType="search"
class="ant-table-filter-icon"
[class.ant-table-filter-open]="dropdown.nzVisible"
nz-dropdown
></i>
<div class="search-box">
<input type="text" nz-input placeholder="Search name" [(ngModel)]="searchValue" />
<button nz-button nzSize="small" nzType="primary" (click)="search()" class="search-button">
Search
</button>
<button nz-button nzSize="small" (click)="reset()">Reset</button>
</div>
</nz-dropdown>
</th>
<th>Age</th>
<th nzShowFilter [nzFilters]="listOfFilterAddress" (nzFilterChange)="filterAddressChange($event)">Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of nzTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
.search-box {
padding: 8px;
}
.search-box input {
width: 188px;
margin-bottom: 8px;
display: block;
}
.search-box button {
width: 90px;
}
.search-button {
margin-right: 8px;
}
`
]
})
export class NzDemoTableCustomFilterPanelComponent {
searchValue = '';
sortName: string | null = null;
sortValue: string | null = null;
listOfFilterAddress = [{ text: 'London', value: 'London' }, { text: 'Sidney', value: 'Sidney' }];
listOfSearchAddress: string[] = [];
listOfData: Array<{ name: string; age: number; address: string; [key: string]: string | number }> = [
{
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
},
{
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park'
}
];
listOfDisplayData = [...this.listOfData];
reset(): void {
this.searchValue = '';
this.search();
}
sort(sortName: string, value: string): void {
this.sortName = sortName;
this.sortValue = value;
this.search();
}
filterAddressChange(value: string[]): void {
this.listOfSearchAddress = value;
this.search();
}
search(): void {
const filterFunc = (item: { name: string; age: number; address: string }) => {
return (
(this.listOfSearchAddress.length
? this.listOfSearchAddress.some(address => item.address.indexOf(address) !== -1)
: true) && item.name.indexOf(this.searchValue) !== -1
);
};
const data = this.listOfData.filter((item: { name: string; age: number; address: string }) => filterFunc(item));
this.listOfDisplayData = data.sort((a, b) =>
this.sortValue === 'ascend'
? a[this.sortName!] > b[this.sortName!]
? 1
: -1
: b[this.sortName!] > a[this.sortName!]
? 1
: -1
);
}
}
远程加载数据
这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。
注意,此示例使用 模拟接口,展示数据可能不准确,请打开网络面板查看请求。
import { HttpClient, HttpParams } from '@angular/common/http';
import { Component, Injectable, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable()
export class RandomUserService {
randomUserUrl = 'https://api.randomuser.me/';
getUsers(
pageIndex: number = 1,
pageSize: number = 10,
sortField: string,
sortOrder: string,
genders: string[]
): Observable<{}> {
let params = new HttpParams()
.append('page', `${pageIndex}`)
.append('results', `${pageSize}`)
.append('sortField', sortField)
.append('sortOrder', sortOrder);
genders.forEach(gender => {
params = params.append('gender', gender);
});
return this.http.get(`${this.randomUserUrl}`, {
params
});
}
constructor(private http: HttpClient) {}
}
@Component({
selector: 'nz-demo-table-ajax',
providers: [RandomUserService],
template: `
<nz-table
#ajaxTable
nzShowSizeChanger
[nzFrontPagination]="false"
[nzData]="listOfData"
[nzLoading]="loading"
[nzTotal]="total"
[(nzPageIndex)]="pageIndex"
[(nzPageSize)]="pageSize"
(nzPageIndexChange)="searchData()"
(nzPageSizeChange)="searchData(true)"
>
<thead (nzSortChange)="sort($event)" nzSingleSort>
<tr>
<th nzShowSort nzSortKey="name">Name</th>
<th nzShowFilter [nzFilters]="filterGender" (nzFilterChange)="updateFilter($event)">Gender</th>
<th nzShowSort nzSortKey="email"><span>Email</span></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of ajaxTable.data">
<td>{{ data.name.first }} {{ data.name.last }}</td>
<td>{{ data.gender }}</td>
<td>{{ data.email }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableAjaxComponent implements OnInit {
pageIndex = 1;
pageSize = 10;
total = 1;
listOfData = [];
loading = true;
sortValue: string | null = null;
sortKey: string | null = null;
filterGender = [{ text: 'male', value: 'male' }, { text: 'female', value: 'female' }];
searchGenderList: string[] = [];
sort(sort: { key: string; value: string }): void {
this.sortKey = sort.key;
this.sortValue = sort.value;
this.searchData();
}
constructor(private randomUserService: RandomUserService) {}
searchData(reset: boolean = false): void {
if (reset) {
this.pageIndex = 1;
}
this.loading = true;
this.randomUserService
.getUsers(this.pageIndex, this.pageSize, this.sortKey!, this.sortValue!, this.searchGenderList)
.subscribe((data: any) => {
this.loading = false;
this.total = 200;
this.listOfData = data.results;
});
}
updateFilter(value: string[]): void {
this.searchGenderList = value;
this.searchData(true);
}
ngOnInit(): void {
this.searchData();
}
}
紧凑型
两种紧凑型的列表,小型列表只用于对话框内。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-size',
template: `
<h4>Middle size table</h4>
<nz-table #middleTable nzSize="middle" [nzData]="data">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of middleTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
<h4>Small size table</h4>
<nz-table #smallTable nzSize="small" [nzData]="data">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of smallTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
h4 {
margin-bottom: 16px;
}
`
]
})
export class NzDemoTableSizeComponent {
data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
}
带边框
添加表格边框线,页头和页脚。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-bordered',
template: `
<nz-table #borderedTable nzBordered nzFooter="Footer" nzTitle="Header" [nzData]="dataSet">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of borderedTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableBorderedComponent {
dataSet = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
}
可展开
当表格内容较多不能一次性完全展示时,可以通过 td
上的 nzExpand
属性展开。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-expand',
template: `
<nz-table #nzTable [nzData]="listOfData">
<thead>
<tr>
<th nzShowExpand></th>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<ng-template ngFor let-data [ngForOf]="nzTable.data">
<tr>
<td nzShowExpand [(nzExpand)]="mapOfExpandData[data.id]"></td>
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
<tr [nzExpand]="mapOfExpandData[data.id]">
<td></td>
<td colspan="3">{{ data.description }}</td>
</tr>
</ng-template>
</tbody>
</nz-table>
`,
styles: []
})
export class NzDemoTableExpandComponent {
mapOfExpandData: { [key: string]: boolean } = {};
listOfData = [
{
id: 1,
name: 'John Brown',
age: 32,
expand: false,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.'
},
{
id: 2,
name: 'Jim Green',
age: 42,
expand: false,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.'
},
{
id: 3,
name: 'Joe Black',
age: 32,
expand: false,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.'
}
];
}
表格行/列合并
像 W3C标准 <table>
一样,使用 colspan
和 rowspan
合并行/列。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-colspan-rowspan',
template: `
<nz-table #colSpanTable [nzData]="listOfData" nzBordered>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th colspan="2">Home phone</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of colSpanTable.data; index as i">
<td>{{ data.name }}</td>
<td [attr.colspan]="i === 4 ? 5 : 1">{{ data.age }}</td>
<td [attr.rowspan]="i === 2 ? 2 : 1" *ngIf="i !== 3 && i !== 4">{{ data.tel }}</td>
<td *ngIf="i !== 4">{{ data.phone }}</td>
<td *ngIf="i !== 4">{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableColspanRowspanComponent {
listOfData = [
{
key: '1',
name: 'John Brown',
age: 32,
tel: '0571-22098909',
phone: 18889898989,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
tel: '0571-22098333',
phone: 18889898888,
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
tel: '0575-22098909',
phone: 18900010002,
address: 'Sidney No. 1 Lake Park'
},
{
key: '4',
name: 'Jim Red',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'London No. 2 Lake Park'
},
{
key: '5',
name: 'Jake White',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'Dublin No. 2 Lake Park'
}
];
}
树形数据展示
表格支持树形数据的展示,可以通过设置 nzIndentSize
以控制每一层的缩进宽度,本例子中提供了树与数组之间的转换函数,实际业务中请根据需求修改。
import { Component, OnInit } from '@angular/core';
export interface TreeNodeInterface {
key: number;
name: string;
age: number;
level: number;
expand: boolean;
address: string;
children?: TreeNodeInterface[];
}
@Component({
selector: 'nz-demo-table-expand-children',
template: `
<nz-table #expandTable [nzData]="listOfMapData">
<thead>
<tr>
<th nzWidth="40%">Name</th>
<th nzWidth="30%">Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let data of expandTable.data">
<ng-container *ngFor="let item of mapOfExpandedData[data.key]">
<tr *ngIf="(item.parent && item.parent.expand) || !item.parent">
<td
[nzIndentSize]="item.level * 20"
[nzShowExpand]="!!item.children"
[(nzExpand)]="item.expand"
(nzExpandChange)="collapse(mapOfExpandedData[data.key], item, $event)"
>
{{ item.name }}
</td>
<td>{{ item.age }}</td>
<td>{{ item.address }}</td>
</tr>
</ng-container>
</ng-container>
</tbody>
</nz-table>
`
})
export class NzDemoTableExpandChildrenComponent implements OnInit {
listOfMapData = [
{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park'
},
{
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: 121,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park'
}
]
},
{
key: 13,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: 1311,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park'
},
{
key: 1312,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park'
}
]
}
]
}
]
},
{
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
mapOfExpandedData: { [key: string]: TreeNodeInterface[] } = {};
collapse(array: TreeNodeInterface[], data: TreeNodeInterface, $event: boolean): void {
if ($event === false) {
if (data.children) {
data.children.forEach(d => {
const target = array.find(a => a.key === d.key)!;
target.expand = false;
this.collapse(array, target, false);
});
} else {
return;
}
}
}
convertTreeToList(root: object): TreeNodeInterface[] {
const stack: any[] = [];
const array: any[] = [];
const hashMap = {};
stack.push({ ...root, level: 0, expand: false });
while (stack.length !== 0) {
const node = stack.pop();
this.visitNode(node, hashMap, array);
if (node.children) {
for (let i = node.children.length - 1; i >= 0; i--) {
stack.push({ ...node.children[i], level: node.level + 1, expand: false, parent: node });
}
}
}
return array;
}
visitNode(node: TreeNodeInterface, hashMap: { [key: string]: any }, array: TreeNodeInterface[]): void {
if (!hashMap[node.key]) {
hashMap[node.key] = true;
array.push(node);
}
}
ngOnInit(): void {
this.listOfMapData.forEach(item => {
this.mapOfExpandedData[item.key] = this.convertTreeToList(item);
});
}
}
固定表头
方便一页内展示大量数据。
需要指定 th
的 nzWidth
属性,否则列头和内容可能不对齐。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-fixed-header',
template: `
<nz-table #headerTable [nzData]="listOfData" [nzPageSize]="50" [nzScroll]="{ y: '240px' }">
<thead>
<tr>
<th nzWidth="150px">Name</th>
<th nzWidth="150px">Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of headerTable.data">
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableFixedHeaderComponent implements OnInit {
listOfData: any[] = [];
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfData.push({
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`
});
}
}
}
固定列
对于列数很多的数据,可以使用 nzLeft
和 nzRight
固定前后的列,横向滚动查看其它数据,需要和 nzScroll.x
配合使用。
固定列使用了
sticky
属性,浏览器支持情况可以参考这里。若列头与内容不对齐或出现列重复,请指定每一列的
th
的宽度nzWidth
。建议指定
nzScroll.x
为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过nzScroll.x
。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-fixed-columns',
template: `
<nz-table #columnTable [nzData]="listOfData" [nzScroll]="{ x: '1100px' }">
<thead>
<tr>
<th nzWidth="100px" nzLeft="0px">Full Name</th>
<th nzWidth="100px" nzLeft="100px">Age</th>
<th nzWidth="100px">Column 1</th>
<th nzWidth="100px">Column 2</th>
<th nzWidth="100px">Column 3</th>
<th nzWidth="100px">Column 4</th>
<th nzWidth="100px">Column 5</th>
<th nzWidth="100px">Column 6</th>
<th nzWidth="100px">Column 7</th>
<th nzRight="100px" nzWidth="100px">Column 8</th>
<th nzWidth="100px" nzRight="0px">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of columnTable.data">
<td nzLeft="0px">{{ data.name }}</td>
<td nzLeft="100px">{{ data.age }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td nzRight="100px">{{ data.address }}</td>
<td nzRight="0px">
<a>action</a>
</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableFixedColumnsComponent {
listOfData = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York'
},
{
key: '2',
name: 'Jim Green',
age: 40,
address: 'London'
}
];
}
固定头和列
适合同时展示有大量数据和数据列。
固定列使用了
sticky
属性,浏览器支持情况可以参考这里。若列头与内容不对齐或出现列重复,请指定列的宽度
nzWidth
。建议指定
nzScroll.x
为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过nzScroll.x
。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-fixed-columns-header',
template: `
<nz-table #fixedTable [nzData]="listOfData" [nzScroll]="{ x: '1150px', y: '240px' }">
<thead>
<tr>
<th nzWidth="150px" nzLeft="0px">Full Name</th>
<th nzWidth="100px" nzLeft="150px">Age</th>
<th nzWidth="100px">Column 1</th>
<th nzWidth="100px">Column 2</th>
<th nzWidth="100px">Column 3</th>
<th nzWidth="100px">Column 4</th>
<th nzWidth="100px">Column 5</th>
<th nzWidth="100px">Column 6</th>
<th nzWidth="100px">Column 7</th>
<th nzWidth="100px">Column 8</th>
<th nzWidth="100px" nzRight="0px">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of fixedTable.data">
<td nzLeft="0px">{{ data.name }}</td>
<td nzLeft="150px">{{ data.age }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td nzRight="0px">
<a>action</a>
</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableFixedColumnsHeaderComponent implements OnInit {
listOfData: any[] = [];
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfData.push({
name: `Edward King ${i}`,
age: 32,
address: `London`
});
}
}
}
表头分组
当使用分组表头时,th
的 nzWidth
方式不再适用,使用 nzWidthConfig
来设定每个分组的宽度
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-grouping-columns',
template: `
<nz-table
#groupingTable
[nzData]="listOfDisplayData"
nzBordered
nzSize="middle"
[nzWidthConfig]="widthConfig"
[nzScroll]="scrollConfig"
>
<thead>
<tr>
<th rowspan="4" nzLeft="0px" nzShowFilter [nzFilters]="filterName" (nzFilterChange)="search($event)">Name</th>
<th colspan="4">Other</th>
<th colspan="2">Company</th>
<th rowspan="4" nzRight="0px">Gender</th>
</tr>
<tr>
<th rowspan="3" nzShowSort [(nzSort)]="sortValue" (nzSortChange)="search(searchName)">Age</th>
<th colspan="3">Address</th>
<th rowspan="3">Company Address</th>
<th rowspan="3">Company Name</th>
</tr>
<tr>
<th rowspan="2">Street</th>
<th colspan="2">Block</th>
</tr>
<tr>
<th>Building</th>
<th>Door No.</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of groupingTable.data">
<td nzLeft="0px">{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.street }}</td>
<td>{{ data.building }}</td>
<td>{{ data.number }}</td>
<td>{{ data.companyAddress }}</td>
<td>{{ data.companyName }}</td>
<td nzRight="0px">{{ data.gender }}</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableGroupingColumnsComponent implements OnInit {
widthConfig = ['100px', '200px', '200px', '100px', '100px', '200px', '200px', '100px'];
scrollConfig = { x: '1200px', y: '240px' };
listOfDisplayData: any[] = [];
listOfData: any[] = [];
sortValue: string | null = null;
filterName = [{ text: 'Joe', value: 'Joe' }, { text: 'John', value: 'John' }];
searchName: string[] = [];
search(searchName: string[]): void {
this.searchName = searchName;
const filterFunc = (item: any) => {
return this.searchName.length ? this.searchName.some(name => item.name.indexOf(name) !== -1) : true;
};
const listOfData = this.listOfData.filter(item => filterFunc(item));
this.listOfDisplayData = listOfData.sort((a, b) =>
this.sortValue === 'ascend' ? (a.age > b.age ? 1 : -1) : b.age > a.age ? 1 : -1
);
}
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfData.push({
name: 'John Brown',
age: i + 1,
street: 'Lake Park',
building: 'C',
number: 2035,
companyAddress: 'Lake Street 42',
companyName: 'SoftLake Co',
gender: 'M'
});
}
this.listOfDisplayData = [...this.listOfData];
}
}
可编辑单元格
定制带单元格编辑功能的表格,自由操作单元格内容。
import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
import { NzInputDirective } from 'ng-zorro-antd';
@Component({
selector: 'nz-demo-table-edit-cell',
template: `
<button nz-button (click)="addRow()" nzType="primary">Add</button>
<nz-table #editRowTable nzBordered [nzData]="listOfData">
<thead>
<tr>
<th nzWidth="30%">Name</th>
<th>Age</th>
<th>Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of editRowTable.data" class="editable-row">
<td>
<div class="editable-cell" *ngIf="editId !== data.id; else editTpl">
<div class="editable-cell-value-wrap" (click)="startEdit(data.id, $event)">
{{ data.name }}
</div>
</div>
<ng-template #editTpl>
<input type="text" nz-input [(ngModel)]="data.name" />
</ng-template>
</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
<td>
<a nz-popconfirm nzTitle="Sure to delete?" (nzOnConfirm)="deleteRow(data.id)">Delete</a>
</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
button {
margin-bottom: 16px;
}
.editable-cell {
position: relative;
}
.editable-cell-value-wrap {
padding: 5px 12px;
cursor: pointer;
}
.editable-row:hover .editable-cell-value-wrap {
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 4px 11px;
}
`
]
})
export class NzDemoTableEditCellComponent implements OnInit {
i = 0;
editId: string | null;
listOfData: any[] = [];
@ViewChild(NzInputDirective, { read: ElementRef }) inputElement: ElementRef;
@HostListener('window:click', ['$event'])
handleClick(e: MouseEvent): void {
if (this.editId && this.inputElement && this.inputElement.nativeElement !== e.target) {
this.editId = null;
}
}
addRow(): void {
this.listOfData = [
...this.listOfData,
{
id: `${this.i}`,
name: `Edward King ${this.i}`,
age: '32',
address: `London, Park Lane no. ${this.i}`
}
];
this.i++;
}
deleteRow(id: string): void {
this.listOfData = this.listOfData.filter(d => d.id !== id);
}
startEdit(id: string, event: MouseEvent): void {
event.preventDefault();
event.stopPropagation();
this.editId = id;
}
ngOnInit(): void {
this.addRow();
this.addRow();
}
}
可编辑行
定制带行编辑功能的表格,自由操作行内容。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-edit-row',
template: `
<nz-table #editRowTable nzBordered [nzData]="listOfData">
<thead>
<tr>
<th nzWidth="25%">Name</th>
<th nzWidth="15%">Age</th>
<th nzWidth="40%">Address</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of editRowTable.data">
<td>
<ng-container *ngIf="!editCache[data.id].edit; else nameInputTpl">
{{ data.name }}
</ng-container>
<ng-template #nameInputTpl>
<input type="text" nz-input [(ngModel)]="editCache[data.id].data.name" />
</ng-template>
</td>
<td>
<ng-container *ngIf="!editCache[data.id].edit; else ageInputTpl">
{{ data.age }}
</ng-container>
<ng-template #ageInputTpl>
<input type="text" nz-input [(ngModel)]="editCache[data.id].data.age" />
</ng-template>
</td>
<td>
<ng-container *ngIf="!editCache[data.id].edit; else addressInputTpl">
{{ data.address }}
</ng-container>
<ng-template #addressInputTpl>
<input type="text" nz-input [(ngModel)]="editCache[data.id].data.address" />
</ng-template>
</td>
<td>
<div class="editable-row-operations">
<ng-container *ngIf="!editCache[data.id].edit; else saveTpl">
<a (click)="startEdit(data.id)">Edit</a>
</ng-container>
<ng-template #saveTpl>
<a (click)="saveEdit(data.id)">Save</a>
<a nz-popconfirm nzTitle="Sure to cancel?" (nzOnConfirm)="cancelEdit(data.id)">Cancel</a>
</ng-template>
</div>
</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
.editable-row-operations a {
margin-right: 8px;
}
`
]
})
export class NzDemoTableEditRowComponent implements OnInit {
editCache: { [key: string]: any } = {};
listOfData: any[] = [];
startEdit(id: string): void {
this.editCache[id].edit = true;
}
cancelEdit(id: string): void {
const index = this.listOfData.findIndex(item => item.id === id);
this.editCache[id] = {
data: { ...this.listOfData[index] },
edit: false
};
}
saveEdit(id: string): void {
const index = this.listOfData.findIndex(item => item.id === id);
Object.assign(this.listOfData[index], this.editCache[id].data);
this.editCache[id].edit = false;
}
updateEditCache(): void {
this.listOfData.forEach(item => {
this.editCache[item.id] = {
edit: false,
data: { ...item }
};
});
}
ngOnInit(): void {
for (let i = 0; i < 100; i++) {
this.listOfData.push({
id: `${i}`,
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`
});
}
this.updateEditCache();
}
}
嵌套子表格
展示每行数据更详细的信息。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-nested-table',
template: `
<nz-table #nestedTable [nzData]="listOfParentData" [nzPageSize]="10">
<thead>
<tr>
<th nzShowExpand></th>
<th>Name</th>
<th>Platform</th>
<th>Version</th>
<th>Upgraded</th>
<th>Creator</th>
<th>Date</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<ng-template ngFor let-data [ngForOf]="nestedTable.data">
<tr>
<td nzShowExpand [(nzExpand)]="data.expand"></td>
<td>{{ data.name }}</td>
<td>{{ data.platform }}</td>
<td>{{ data.version }}</td>
<td>{{ data.upgradeNum }}</td>
<td>{{ data.creator }}</td>
<td>{{ data.createdAt }}</td>
<td>
<a>Publish</a>
</td>
</tr>
<tr [nzExpand]="data.expand">
<td></td>
<td colspan="7">
<nz-table #innerTable [nzData]="listOfChildrenData" nzSize="middle" [nzShowPagination]="false">
<thead>
<tr>
<th>Date</th>
<th>Name</th>
<th>Status</th>
<th>Upgrade Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of innerTable.data">
<td>{{ data.date }}</td>
<td>{{ data.name }}</td>
<td>
<nz-badge [nzStatus]="'success'" [nzText]="'Finished'"></nz-badge>
</td>
<td>{{ data.upgradeNum }}</td>
<td>
<span class="table-operation">
<nz-dropdown>
<a nz-dropdown class="operation"> Pause <i nz-icon type="down"></i> </a>
<ul nz-menu>
<li nz-menu-item>
<a>Action 1</a>
</li>
<li nz-menu-item>
<a>Action 2</a>
</li>
</ul>
</nz-dropdown>
<nz-divider nzType="vertical"></nz-divider>
<a class="operation">Stop</a>
<nz-divider nzType="vertical"></nz-divider>
<a>More</a>
</span>
</td>
</tr>
</tbody>
</nz-table>
</td>
</tr>
</ng-template>
</tbody>
</nz-table>
`
})
export class NzDemoTableNestedTableComponent implements OnInit {
listOfParentData: any[] = [];
listOfChildrenData: any[] = [];
ngOnInit(): void {
for (let i = 0; i < 3; ++i) {
this.listOfParentData.push({
key: i,
name: 'Screem',
platform: 'iOS',
version: '10.3.4.5654',
upgradeNum: 500,
creator: 'Jack',
createdAt: '2014-12-24 23:12:00',
expand: false
});
}
for (let i = 0; i < 3; ++i) {
this.listOfChildrenData.push({
key: i,
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56'
});
}
}
}
虚拟滚动
虚拟滚动,结合 cdk scrolling 的虚拟滚动,用于巨量数据加载。可以通过获得 cdkVirtualScrollViewport
进行进一步操作,见本示例及 API。
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { NzTableComponent } from 'ng-zorro-antd';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'nz-demo-table-virtual',
template: `
<button nz-button (click)="scrollToIndex(200)">Scroll To Index 200</button>
<br />
<br />
<nz-table
#virtualTable
nzVirtualScroll
[nzVirtualItemSize]="54"
[nzData]="listOfData"
[nzFrontPagination]="false"
[nzShowPagination]="false"
[nzScroll]="{ x: '1300px', y: '240px' }"
>
<thead>
<tr>
<th nzWidth="200px" nzLeft="0px">Full Name</th>
<th nzWidth="100px" nzLeft="200px">Age</th>
<th nzWidth="100px">Index</th>
<th nzWidth="100px">Column 1</th>
<th nzWidth="100px">Column 2</th>
<th nzWidth="100px">Column 3</th>
<th nzWidth="100px">Column 4</th>
<th nzWidth="100px">Column 5</th>
<th nzWidth="100px">Column 6</th>
<th nzWidth="100px">Column 7</th>
<th nzWidth="100px">Column 8</th>
<th nzWidth="100px" nzRight="0px">Action</th>
</tr>
</thead>
<tbody>
<ng-template nz-virtual-scroll let-data let-index="index">
<tr>
<td nzLeft="0px">{{ data.name }} {{ index }}</td>
<td nzLeft="200px">{{ data.age }}</td>
<td>{{ data.index }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td>{{ data.address }}</td>
<td nzRight="0px">
<a>action</a>
</td>
</tr>
</ng-template>
</tbody>
</nz-table>
`
})
export class NzDemoTableVirtualComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('virtualTable') nzTableComponent: NzTableComponent;
private destroy$ = new Subject();
listOfData: any[] = [];
scrollToIndex(index: number): void {
this.nzTableComponent.cdkVirtualScrollViewport.scrollToIndex(index);
}
ngOnInit(): void {
for (let i = 0; i < 20000; i++) {
this.listOfData.push({
index: i,
name: `Edward King`,
age: 32,
address: `London`
});
}
}
ngAfterViewInit(): void {
this.nzTableComponent.cdkVirtualScrollViewport.scrolledIndexChange
.pipe(takeUntil(this.destroy$))
.subscribe(data => {
console.log('scroll index to', data);
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
拖拽排序
使用自定义元素,我们可以集成 cdk drag-drop 来实现拖拽排序。
import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-drag-sorting',
template: `
<nz-table [nzData]="listOfData" [nzFrontPagination]="false" [nzShowPagination]="false">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody cdkDropList (cdkDropListDropped)="drop($event)">
<tr *ngFor="let data of listOfData" cdkDrag>
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
</tr>
</tbody>
</nz-table>
`,
styles: [
`
::ng-deep .cdk-drag-preview {
display: table;
}
::ng-deep .cdk-drag-placeholder {
opacity: 0;
}
`
]
})
export class NzDemoTableDragSortingComponent {
listOfData = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park'
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park'
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park'
}
];
drop(event: CdkDragDrop<string[]>): void {
moveItemInArray(this.listOfData, event.previousIndex, event.currentIndex);
}
}
模板用法
模板模式,显示内容仅由模板内容控制,不再需要向 nzData
传入数据,完全像普通 table
一样使用,使用 ant-design 的样式。
import { Component } from '@angular/core';
@Component({
selector: 'nz-demo-table-template',
template: `
<nz-table nzTemplateMode>
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Country</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alfreds Futterkiste</td>
<td>Maria Anders</td>
<td>Germany</td>
</tr>
<tr>
<td>Centro comercial Moctezuma</td>
<td>Francisco Chang</td>
<td>Mexico</td>
</tr>
<tr>
<td>Ernst Handel</td>
<td>Roland Mendel</td>
<td>Austria</td>
</tr>
<tr>
<td>Island Trading</td>
<td>Helen Bennett</td>
<td>UK</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Yoshi Tannamuri</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Giovanni Rovelli</td>
<td>Italy</td>
</tr>
</tbody>
</nz-table>
`
})
export class NzDemoTableTemplateComponent {}
动态控制表格属性
选择不同配置组合查看效果。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'nz-demo-table-dynamic-settings',
template: `
<div class="components-table-demo-control-bar">
<form nz-form nzLayout="inline">
<nz-form-item>
<nz-form-label><label>Bordered</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="bordered" name="bordered"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Loading</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="loading" name="loading"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Pagination</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="pagination" name="pagination"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>PageSizeChanger</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="sizeChanger" name="sizeChanger"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Title</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="title" name="title"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Column Header</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="header" name="header"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Footer</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="footer" name="footer"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Expandable</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="expandable" name="expandable"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Checkbox</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="checkbox" name="checkbox"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Fixed Header</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="fixHeader" name="fixHeader"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>No Result</label></nz-form-label>
<nz-form-control
><nz-switch [(ngModel)]="noResult" (ngModelChange)="noResultChange($event)" name="noResult"></nz-switch
></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Simple Pagination</label></nz-form-label>
<nz-form-control><nz-switch [(ngModel)]="simple" name="simple"></nz-switch></nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Size</label></nz-form-label>
<nz-form-control>
<nz-radio-group [(ngModel)]="size" name="size">
<label nz-radio-button nzValue="default">Default</label>
<label nz-radio-button nzValue="middle">Middle</label>
<label nz-radio-button nzValue="small">Small</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label><label>Pagination Position</label></nz-form-label>
<nz-form-control>
<nz-radio-group [(ngModel)]="position" name="position">
<label nz-radio-button nzValue="top">Top</label>
<label nz-radio-button nzValue="bottom">Bottom</label>
<label nz-radio-button nzValue="both">Both</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
</form>
</div>
<nz-table
#dynamicTable
[nzScroll]="fixHeader ? { y: '240px' } : null"
[nzData]="listOfData"
[nzBordered]="bordered"
[nzSimple]="simple"
[nzLoading]="loading"
[nzPaginationPosition]="position"
[nzShowSizeChanger]="sizeChanger"
[nzFrontPagination]="pagination"
[nzShowPagination]="pagination"
[nzFooter]="footer ? 'Here is Footer' : null"
[nzTitle]="title ? 'Here is Title' : null"
[nzSize]="size"
(nzCurrentPageDataChange)="currentPageDataChange($event)"
>
<thead>
<tr *ngIf="header">
<th nzWidth="50px" nzShowExpand *ngIf="expandable"></th>
<th
nzWidth="62px"
nzShowCheckbox
*ngIf="checkbox"
[(nzChecked)]="allChecked"
[nzIndeterminate]="indeterminate"
(nzCheckedChange)="checkAll($event)"
></th>
<th nzWidth="150px">Name</th>
<th nzWidth="70px">Age</th>
<th>Address</th>
<th nzWidth="260px">Action</th>
</tr>
</thead>
<tbody>
<ng-template ngFor let-data [ngForOf]="dynamicTable.data">
<tr>
<td nzShowExpand *ngIf="expandable" [(nzExpand)]="data.expand"></td>
<td nzShowCheckbox *ngIf="checkbox" [(nzChecked)]="data.checked" (nzCheckedChange)="refreshStatus()"></td>
<td>{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ data.address }}</td>
<td>
<a href="#">Action 一 {{ data.name }}</a>
<nz-divider nzType="vertical"></nz-divider>
<a href="#">Delete</a>
</td>
</tr>
<tr [nzExpand]="data.expand && expandable">
<td></td>
<td [attr.colspan]="checkbox ? 5 : 4">{{ data.description }}</td>
</tr>
</ng-template>
</tbody>
</nz-table>
`,
styles: [
`
.components-table-demo-control-bar {
margin-bottom: 12px;
}
.nz-form-item {
margin-right: 16px;
margin-bottom: 8px;
}
`
]
})
export class NzDemoTableDynamicSettingsComponent implements OnInit {
listOfData: any[] = [];
bordered = false;
loading = false;
sizeChanger = false;
pagination = true;
header = true;
title = true;
footer = true;
fixHeader = false;
size = 'small';
expandable = true;
checkbox = true;
allChecked = false;
indeterminate = false;
displayData: any[] = [];
simple = false;
noResult = false;
position = 'bottom';
currentPageDataChange(
$event: Array<{
name: string;
age: number;
address: string;
checked: boolean;
expand: boolean;
description: string;
}>
): void {
this.displayData = $event;
this.refreshStatus();
}
refreshStatus(): void {
const validData = this.displayData.filter(value => !value.disabled);
const allChecked = validData.length > 0 && validData.every(value => value.checked === true);
const allUnChecked = validData.every(value => !value.checked);
this.allChecked = allChecked;
this.indeterminate = !allChecked && !allUnChecked;
}
checkAll(value: boolean): void {
this.displayData.forEach(data => {
if (!data.disabled) {
data.checked = value;
}
});
this.refreshStatus();
}
ngOnInit(): void {
for (let i = 1; i <= 100; i++) {
this.listOfData.push({
name: 'John Brown',
age: `${i}2`,
address: `New York No. ${i} Lake Park`,
description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
checked: false,
expand: false
});
}
}
noResultChange(status: boolean): void {
this.listOfData = [];
if (!status) {
this.ngOnInit();
}
}
}
API
单独引入此组件
想要了解更多关于单独引入组件的内容,可以在快速上手页面进行查看。
import { NzTableModule } from 'ng-zorro-antd';
nz-tablecomponent
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzData] | 数据数组 | any[] | - |
[nzFrontPagination] | 是否在前端对数据进行分页,如果在服务器分页数据或者需要在前端显示全部数据时传入 false | boolean | true |
[nzTotal] | 当前总数据,在服务器渲染时需要传入 | number | - |
[nzPageIndex] | 当前页码,可双向绑定 | number | - |
[nzPageSize] | 每页展示多少数据,可双向绑定 | number | - |
[nzShowPagination] | 是否显示分页器 | boolean | true |
[nzPaginationPosition] | 指定分页显示的位置 | 'top'|'bottom'|'both' | bottom |
[nzBordered] | 是否展示外边框和列边框 | boolean | false |
[nzWidthConfig] | 表头分组时指定每列宽度,与 th 的 nzWidth 不可混用 | string[] | - |
[nzSize] | 正常或迷你类型 | 'middle'|'small'|'default' | 'default' |
[nzLoading] | 页面是否加载中 | boolean | false |
[nzLoadingIndicator] | 加载指示符 | TemplateRef<void> | - |
[nzLoadingDelay] | 延迟显示加载效果的时间(防止闪烁) | number | 0 |
[nzScroll] | 横向或纵向支持滚动,也可用于指定滚动区域的宽高度:{ x: "300px", y: "300px" } | object | - |
[nzTitle] | 表格标题 | string|TemplateRef<void> | - |
[nzFooter] | 表格尾部 | string|TemplateRef<void> | - |
[nzNoResult] | 无数据时显示内容 | string|TemplateRef<void> | - |
[nzPageSizeOptions] | 页数选择器可选值 | number[] | [ 10, 20, 30, 40, 50 ] |
[nzShowQuickJumper] | 是否可以快速跳转至某页 | boolean | false |
[nzShowSizeChanger] | 是否可以改变 nzPageSize | boolean | false |
[nzShowTotal] | 用于显示数据总量和当前数据范围,用法参照 Pagination 组件 | TemplateRef<{ $implicit: number, range: [ number, number ] }> | - |
[nzItemRender] | 用于自定义页码的结构,用法参照 Pagination 组件 | TemplateRef<{ $implicit: 'page'|'prev'|'next', page: number }> | - |
[nzHideOnSinglePage] | 只有一页时是否隐藏分页器 | boolean | false |
[nzSimple] | 当添加该属性时,显示为简单分页 | boolean | - |
[nzVirtualScroll] | 是否启用虚拟滚动模式,与 [nzScroll] 配合使用 | boolean | false |
[nzVirtualItemSize] | 虚拟滚动时每一列的高度,与 cdk itemSize 相同 | number | 0 |
[nzVirtualMaxBufferPx] | 缓冲区最大像素高度,与 cdk maxBufferPx 相同 | number | 200 |
[nzVirtualMinBufferPx] | 缓冲区最小像素高度,低于该值时将加载新结构,与 cdk minBufferPx 相同 | number | 100 |
(nzPageIndexChange) | 当前页码改变时的回调函数 | EventEmitter<number> | - |
(nzPageSizeChange) | 页数改变时的回调函数 | EventEmitter<number> | - |
(nzCurrentPageDataChange) | 当前页面展示数据改变的回调函数 | EventEmitter<any[]> | - |
th
勾选属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowCheckbox] | 是否添加checkbox | boolean | - |
[nzDisabled] | checkbox 是否禁用 | boolean | - |
[nzIndeterminate] | checkbox indeterminate 状态 | boolean | - |
[nzChecked] | checkbox 是否被选中,可双向绑定 | boolean | - |
(nzCheckedChange) | 选中的回调 | EventEmitter<boolean> | - |
下拉选择属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowRowSelection] | 是否显示下拉选择 | boolean | - |
[nzSelections] | 下拉选择的内容 text 及回调函数 onSelect | Array<{ text: string, onSelect: any }> | - |
排序属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowSort] | 是否显示排序 | boolean | - |
[nzSortKey] | 排序key,非受控模式使用,与 thead 中 nzSortChange 配合使用 | string | - |
[nzSort] | 当前排序状态,受控模式使用,可双向绑定 | 'descend'|'ascend'|null | null |
(nzSortChange) | 排序状态改变回调,受控模式使用 | EventEmitter<'descend'|'ascend'|null> | - |
过滤属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowFilter] | 是否显示过滤 | boolean | - |
[nzFilters] | 过滤器内容, 显示数据 text ,回调函数传出 value ,设置 byDefault 以默认应用过滤规则 | Array<{ text: string; value: any; byDefault?: boolean }> | - |
[nzFilterMultiple] | 是否为多选过滤器 | boolean | true |
(nzFilterChange) | 过滤器内容选择的 value 数据回调 | EventEmitter<any[]|any> | - |
样式属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzWidth] | 指定该列宽度,表头未分组时可用 | string | - |
[nzLeft] | 左侧距离,用于固定左侧列 | string | - |
[nzRight] | 右侧距离,用于固定右侧列 | string | - |
[nzAlign] | 设置列内容的对齐方式 | 'left'|'right'|'center' | - |
其他
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzExpand] | 当前列是否包含展开按钮 | boolean | - |
td
勾选属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowCheckbox] | 是否添加checkbox | boolean | - |
[nzDisabled] | checkbox 是否禁用 | boolean | - |
[nzIndeterminate] | checkbox indeterminate 状态 | boolean | - |
[nzChecked] | checkbox 是否被选中,可双向绑定 | boolean | - |
(nzCheckedChange) | 选中的回调 | EventEmitter<boolean> | - |
展开属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzShowExpand] | 是否显示展开按钮 | boolean | - |
[nzExpand] | 当前展开按钮状态,可双向绑定 | boolean | - |
(nzExpandChange) | 当前展开按钮状态改变回调函数 | EventEmitter<boolean> | - |
样式属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzLeft] | 左侧距离,用于固定左侧列 | string | - |
[nzRight] | 右侧距离,用于固定右侧列 | string | - |
[nzAlign] | 设置列内容的对齐方式 | 'left'|'right'|'center' | - |
其他
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzIndentSize] | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | - |
thead
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzSingleSort] | 是否单列排序模式,非受控排序下使用 | boolean | false |
(nzSortChange) | 排序改变时的回调函数,需要与 th 上的 nzSortKey 同时使用,非受控排序下使用 | EventEmitter<{ nzSortKey: string, value: 'descend'|'ascend'|null }> | - |
tr
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
[nzExpand] | 当前列是否展开,与 td 上的 nzExpand 属性配合使用 | boolean | - |
[nz-virtual-scroll]directive
虚拟滚动时配合 ng-template
使用, 格式为: TemplateRef<{ $implicit: any, index: number }>
.
注意
按照 Angular 的设计,当需要对 nzData
中的数据进行增删时需要使用以下操作,使用 push
或者 splice
修改 nzData
的数据不会生效
// 增加数据
this.dataSet = [ ...this.dataSet, {
key : `${this.i}`,
name : `Edward King ${this.i}`,
age : '32',
address: `London, Park Lane no. ${this.i}`
}];
// 删除数据
this.dataSet = this.dataSet.filter(d => d.key !== i);