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"><buttonnz-button[disabled]="numberOfChecked === 0"[nzType]="'primary'"[nzLoading]="isOperating"(click)="operateData()">Reload</button><span *ngIf="numberOfChecked">Selected {{ numberOfChecked }} items</span></div><nz-table#rowSelectionTablenzShowPaginationnzShowSizeChanger[nzData]="listOfAllData"(nzCurrentPageDataChange)="currentPageDataChange($event)"><thead><tr><thnzShowCheckbox[(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"><tdnzShowCheckbox[(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#rowSelectionTablenzShowSizeChanger[nzData]="listOfAllData"(nzCurrentPageDataChange)="currentPageDataChange($event)"><thead><tr><thnzShowCheckboxnzShowRowSelection[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><thnzShowSortnzSortKey="name"nzShowFilter[nzFilters]="listOfName"(nzFilterChange)="filter($event, searchAddress)">Name</th><th nzShowSort nzSortKey="age">Age</th><thnzShowSortnzSortKey="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><thnzShowSortnzSortKey="name"nzShowFilter[nzFilters]="listOfName"(nzFilterChange)="filter($event, searchAddress)">Name</th><th nzShowSort nzSortKey="age">Age</th><thnzShowSortnzSortKey="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><thnzShowSortnzShowFilter[(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><thnzShowSortnzShowFilter[(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><inz-iconnzType="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#ajaxTablenzShowSizeChanger[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"nzBorderednzSize="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#virtualTablenzVirtualScroll[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><thnzWidth="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);