Transfer穿梭框

双栏穿梭选择框。

何时使用

用直观的方式在两栏中移动元素,完成选择行为。

选择一个或以上的选项后,点击对应的方向键,可以把选中的选项移动到另一栏。 其中,左边一栏为 source,右边一栏为 target,API 的设计也反映了这两个概念。

  1. import { NzTransferModule } from 'ng-zorro-antd/transfer';

代码演示Transfer穿梭框 - 图2

Transfer穿梭框 - 图3

基本用法

最基本的用法,展示了 nzDataSource 每行的渲染函数 nzRender 以及回调函数 nzChangenzSelectChange 的用法。

  1. import { Component, OnInit } from '@angular/core';
  2. import { TransferItem } from 'ng-zorro-antd/transfer';
  3. @Component({
  4. selector: 'nz-demo-transfer-basic',
  5. template: `
  6. <nz-transfer
  7. [nzDataSource]="list"
  8. [nzDisabled]="disabled"
  9. [nzTitles]="['Source', 'Target']"
  10. (nzSelectChange)="select($event)"
  11. [nzSelectedKeys]="['0', '2']"
  12. (nzChange)="change($event)"
  13. >
  14. </nz-transfer>
  15. <div style="margin-top: 8px;">
  16. <nz-switch [(ngModel)]="disabled" nzCheckedChildren="disabled" nzUnCheckedChildren="disabled"></nz-switch>
  17. <div></div>
  18. </div>
  19. `
  20. })
  21. export class NzDemoTransferBasicComponent implements OnInit {
  22. list: TransferItem[] = [];
  23. disabled = false;
  24. ngOnInit(): void {
  25. for (let i = 0; i < 20; i++) {
  26. this.list.push({
  27. key: i.toString(),
  28. title: `content${i + 1}`,
  29. disabled: i % 3 < 1
  30. });
  31. }
  32. [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  33. }
  34. select(ret: {}): void {
  35. console.log('nzSelectChange', ret);
  36. }
  37. change(ret: {}): void {
  38. console.log('nzChange', ret);
  39. }
  40. }

Transfer穿梭框 - 图4

带搜索框

带搜索框的穿梭框,可以自定义搜索函数。

  1. import { Component, OnInit } from '@angular/core';
  2. import { TransferItem } from 'ng-zorro-antd/transfer';
  3. @Component({
  4. selector: 'nz-demo-transfer-search',
  5. template: `
  6. <nz-transfer
  7. [nzDataSource]="list"
  8. [nzDisabled]="disabled"
  9. nzShowSearch
  10. [nzFilterOption]="filterOption"
  11. (nzSearchChange)="search($event)"
  12. (nzSelectChange)="select($event)"
  13. (nzChange)="change($event)"
  14. >
  15. </nz-transfer>
  16. <div style="margin-top: 8px;">
  17. <nz-switch [(ngModel)]="disabled" nzCheckedChildren="disabled" nzUnCheckedChildren="disabled"></nz-switch>
  18. <div></div>
  19. </div>
  20. `
  21. })
  22. export class NzDemoTransferSearchComponent implements OnInit {
  23. list: TransferItem[] = [];
  24. disabled = false;
  25. ngOnInit(): void {
  26. for (let i = 0; i < 20; i++) {
  27. this.list.push({
  28. key: i.toString(),
  29. title: `content${i + 1}`,
  30. description: `description of content${i + 1}`,
  31. direction: Math.random() * 2 > 1 ? 'right' : undefined
  32. });
  33. }
  34. }
  35. // tslint:disable-next-line:no-any
  36. filterOption(inputValue: string, item: any): boolean {
  37. return item.description.indexOf(inputValue) > -1;
  38. }
  39. search(ret: {}): void {
  40. console.log('nzSearchChange', ret);
  41. }
  42. select(ret: {}): void {
  43. console.log('nzSelectChange', ret);
  44. }
  45. change(ret: {}): void {
  46. console.log('nzChange', ret);
  47. }
  48. }

Transfer穿梭框 - 图5

高级用法

穿梭框高级用法,可配置操作文案,可定制宽高,可对底部进行自定义渲染。

  1. import { Component, OnInit } from '@angular/core';
  2. import { NzMessageService } from 'ng-zorro-antd/message';
  3. import { TransferItem } from 'ng-zorro-antd/transfer';
  4. @Component({
  5. selector: 'nz-demo-transfer-advanced',
  6. template: `
  7. <nz-transfer
  8. [nzDataSource]="list"
  9. nzShowSearch
  10. [nzOperations]="['to right', 'to left']"
  11. [nzListStyle]="{ 'width.px': 250, 'height.px': 300 }"
  12. [nzRender]="render"
  13. [nzFooter]="footer"
  14. (nzSelectChange)="select($event)"
  15. (nzChange)="change($event)"
  16. >
  17. <ng-template #render let-item> {{ item.title }}-{{ item.description }} </ng-template>
  18. <ng-template #footer let-direction>
  19. <button nz-button (click)="reload(direction)" [nzSize]="'small'" style="float: right; margin: 5px;">
  20. reload
  21. </button>
  22. </ng-template>
  23. </nz-transfer>
  24. `
  25. })
  26. export class NzDemoTransferAdvancedComponent implements OnInit {
  27. list: TransferItem[] = [];
  28. ngOnInit(): void {
  29. this.getData();
  30. }
  31. getData(): void {
  32. const ret: TransferItem[] = [];
  33. for (let i = 0; i < 20; i++) {
  34. ret.push({
  35. key: i.toString(),
  36. title: `content${i + 1}`,
  37. description: `description of content${i + 1}`,
  38. direction: Math.random() * 2 > 1 ? 'right' : undefined
  39. });
  40. }
  41. this.list = ret;
  42. }
  43. reload(direction: string): void {
  44. this.getData();
  45. this.msg.success(`your clicked ${direction}!`);
  46. }
  47. select(ret: {}): void {
  48. console.log('nzSelectChange', ret);
  49. }
  50. change(ret: {}): void {
  51. console.log('nzChange', ret);
  52. }
  53. constructor(public msg: NzMessageService) {}
  54. }

Transfer穿梭框 - 图6

自定义渲染行数据

自定义渲染每一个 Transfer Item,可用于渲染复杂数据。

  1. import { Component, OnInit } from '@angular/core';
  2. import { NzMessageService } from 'ng-zorro-antd/message';
  3. import { TransferItem } from 'ng-zorro-antd/transfer';
  4. @Component({
  5. selector: 'nz-demo-transfer-custom-item',
  6. template: `
  7. <nz-transfer
  8. [nzDataSource]="list"
  9. [nzListStyle]="{ 'width.px': 300, 'height.px': 300 }"
  10. [nzRender]="render"
  11. (nzSelectChange)="select($event)"
  12. (nzChange)="change($event)"
  13. >
  14. <ng-template #render let-item> <i nz-icon nzType="{{ item.icon }}"></i> {{ item.title }} </ng-template>
  15. </nz-transfer>
  16. `
  17. })
  18. export class NzDemoTransferCustomItemComponent implements OnInit {
  19. list: Array<TransferItem & { description: string; icon: string }> = [];
  20. ngOnInit(): void {
  21. this.getData();
  22. }
  23. getData(): void {
  24. const ret: Array<TransferItem & { description: string; icon: string }> = [];
  25. for (let i = 0; i < 20; i++) {
  26. ret.push({
  27. key: i.toString(),
  28. title: `content${i + 1}`,
  29. description: `description of content${i + 1}`,
  30. direction: Math.random() * 2 > 1 ? 'right' : undefined,
  31. icon: `frown-o`
  32. });
  33. }
  34. this.list = ret;
  35. }
  36. select(ret: {}): void {
  37. console.log('nzSelectChange', ret);
  38. }
  39. change(ret: {}): void {
  40. console.log('nzChange', ret);
  41. }
  42. constructor(public msg: NzMessageService) {}
  43. }

Transfer穿梭框 - 图7

二次校验

利用 nzCanMove 允许在穿梭过程中二次校验;示例默认向右移时强制选中的第一项不可穿梭。

  1. import { Component, OnInit } from '@angular/core';
  2. import { TransferCanMove, TransferItem } from 'ng-zorro-antd/transfer';
  3. import { Observable, of } from 'rxjs';
  4. import { delay } from 'rxjs/operators';
  5. @Component({
  6. selector: 'nz-demo-transfer-can-move',
  7. template: `
  8. <nz-transfer [nzDataSource]="list" [nzCanMove]="canMove" (nzSelectChange)="select($event)" (nzChange)="change($event)"> </nz-transfer>
  9. `
  10. })
  11. export class NzDemoTransferCanMoveComponent implements OnInit {
  12. list: TransferItem[] = [];
  13. ngOnInit(): void {
  14. for (let i = 0; i < 20; i++) {
  15. this.list.push({
  16. key: i.toString(),
  17. title: `content${i + 1}`,
  18. disabled: i % 3 < 1
  19. });
  20. }
  21. [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  22. }
  23. canMove(arg: TransferCanMove): Observable<TransferItem[]> {
  24. if (arg.direction === 'right' && arg.list.length > 0) {
  25. arg.list.splice(0, 1);
  26. }
  27. // or
  28. // if (arg.direction === 'right' && arg.list.length > 0) delete arg.list[0];
  29. return of(arg.list).pipe(delay(1000));
  30. }
  31. select(ret: {}): void {
  32. console.log('nzSelectChange', ret);
  33. }
  34. change(ret: {}): void {
  35. console.log('nzChange', ret);
  36. }
  37. }

Transfer穿梭框 - 图8

表格穿梭框

使用 Table 组件作为自定义渲染列表。

  1. import { Component, OnInit } from '@angular/core';
  2. import { TransferChange, TransferItem, TransferSelectChange } from 'ng-zorro-antd/transfer';
  3. @Component({
  4. selector: 'nz-demo-transfer-table-transfer',
  5. template: `
  6. <nz-transfer
  7. [nzDataSource]="list"
  8. [nzDisabled]="disabled"
  9. [nzShowSearch]="showSearch"
  10. [nzShowSelectAll]="false"
  11. [nzRenderList]="[renderList, renderList]"
  12. (nzSelectChange)="select($event)"
  13. (nzChange)="change($event)"
  14. >
  15. <ng-template
  16. #renderList
  17. let-items
  18. let-direction="direction"
  19. let-stat="stat"
  20. let-disabled="disabled"
  21. let-onItemSelectAll="onItemSelectAll"
  22. let-onItemSelect="onItemSelect"
  23. >
  24. <nz-table #t [nzData]="items" nzSize="small">
  25. <thead>
  26. <tr>
  27. <th
  28. [nzDisabled]="disabled"
  29. [nzChecked]="stat.checkAll"
  30. [nzIndeterminate]="stat.checkHalf"
  31. (nzCheckedChange)="onItemSelectAll($event)"
  32. ></th>
  33. <th>Name</th>
  34. <th *ngIf="direction === 'left'">Tag</th>
  35. <th>Description</th>
  36. </tr>
  37. </thead>
  38. <tbody>
  39. <tr *ngFor="let data of t.data" (click)="onItemSelect(data)">
  40. <td [nzChecked]="data.checked" [nzDisabled]="disabled || data.disabled" (nzCheckedChange)="onItemSelect(data)"></td>
  41. <td>{{ data.title }}</td>
  42. <td *ngIf="direction === 'left'">
  43. <nz-tag>{{ data.tag }}</nz-tag>
  44. </td>
  45. <td>{{ data.description }}</td>
  46. </tr>
  47. </tbody>
  48. </nz-table>
  49. </ng-template>
  50. </nz-transfer>
  51. <div style="margin-top: 8px;">
  52. <nz-switch [(ngModel)]="disabled" nzCheckedChildren="disabled" nzUnCheckedChildren="disabled"></nz-switch>
  53. <nz-switch [(ngModel)]="showSearch" nzCheckedChildren="showSearch" nzUnCheckedChildren="showSearch"></nz-switch>
  54. </div>
  55. `
  56. })
  57. export class NzDemoTransferTableTransferComponent implements OnInit {
  58. list: TransferItem[] = [];
  59. disabled = false;
  60. showSearch = false;
  61. ngOnInit(): void {
  62. for (let i = 0; i < 20; i++) {
  63. this.list.push({
  64. key: i.toString(),
  65. title: `content${i + 1}`,
  66. description: `description of content${i + 1}`,
  67. disabled: i % 4 === 0,
  68. tag: ['cat', 'dog', 'bird'][i % 3]
  69. });
  70. }
  71. [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  72. }
  73. select(ret: TransferSelectChange): void {
  74. console.log('nzSelectChange', ret);
  75. }
  76. change(ret: TransferChange): void {
  77. console.log('nzChange', ret);
  78. const listKeys = ret.list.map(l => l.key);
  79. const hasOwnKey = (e: TransferItem) => e.hasOwnProperty('key');
  80. this.list = this.list.map(e => {
  81. if (listKeys.includes(e.key) && hasOwnKey(e)) {
  82. if (ret.to === 'left') {
  83. delete e.hide;
  84. } else if (ret.to === 'right') {
  85. e.hide = false;
  86. }
  87. }
  88. return e;
  89. });
  90. }
  91. }

Transfer穿梭框 - 图9

树穿梭框

使用 Tree 组件作为自定义渲染列表。

  1. import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
  2. import { NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd/core/tree';
  3. import { TransferChange } from 'ng-zorro-antd/transfer';
  4. import { NzTreeComponent } from 'ng-zorro-antd/tree';
  5. @Component({
  6. selector: 'nz-demo-transfer-tree-transfer',
  7. template: `
  8. <nz-transfer [nzDataSource]="list" [nzShowSelectAll]="false" [nzRenderList]="[leftRenderList, null]" (nzChange)="change($event)">
  9. <ng-template #leftRenderList let-items let-onItemSelectAll="onItemSelectAll" let-onItemSelect="onItemSelect">
  10. <nz-tree #tree [nzData]="treeData" nzExpandAll nzBlockNode>
  11. <ng-template #nzTreeTemplate let-node>
  12. <span
  13. class="ant-tree-checkbox"
  14. [class.ant-tree-checkbox-disabled]="node.isDisabled"
  15. [class.ant-tree-checkbox-checked]="node.isChecked"
  16. (click)="checkBoxChange(node, onItemSelect)"
  17. >
  18. <span class="ant-tree-checkbox-inner"></span>
  19. </span>
  20. <span (click)="checkBoxChange(node, onItemSelect)" class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open">{{
  21. node.title
  22. }}</span>
  23. </ng-template>
  24. </nz-tree>
  25. </ng-template>
  26. </nz-transfer>
  27. `,
  28. changeDetection: ChangeDetectionStrategy.OnPush
  29. })
  30. export class NzDemoTransferTreeTransferComponent {
  31. @ViewChild('tree', { static: true }) tree!: NzTreeComponent;
  32. list: NzTreeNodeOptions[] = [
  33. { key: '1', id: 1, parentid: 0, title: 'parent 1' },
  34. { key: '2', id: 2, parentid: 1, title: 'leaf 1-1', disabled: true, isLeaf: true },
  35. { key: '3', id: 3, parentid: 1, title: 'leaf 1-2', isLeaf: true }
  36. ];
  37. treeData = this.generateTree(this.list);
  38. checkedNodeList: NzTreeNode[] = [];
  39. private generateTree(arr: NzTreeNodeOptions[]): NzTreeNodeOptions[] {
  40. const tree: NzTreeNodeOptions[] = [];
  41. // tslint:disable-next-line:no-any
  42. const mappedArr: any = {};
  43. let arrElem: NzTreeNodeOptions;
  44. let mappedElem: NzTreeNodeOptions;
  45. for (let i = 0, len = arr.length; i < len; i++) {
  46. arrElem = arr[i];
  47. mappedArr[arrElem.id] = { ...arrElem };
  48. mappedArr[arrElem.id].children = [];
  49. }
  50. for (const id in mappedArr) {
  51. if (mappedArr.hasOwnProperty(id)) {
  52. mappedElem = mappedArr[id];
  53. if (mappedElem.parentid) {
  54. mappedArr[mappedElem.parentid].children.push(mappedElem);
  55. } else {
  56. tree.push(mappedElem);
  57. }
  58. }
  59. }
  60. return tree;
  61. }
  62. checkBoxChange(node: NzTreeNode, onItemSelect: (item: NzTreeNodeOptions) => void): void {
  63. if (node.isDisabled) {
  64. return;
  65. }
  66. node.isChecked = !node.isChecked;
  67. if (node.isChecked) {
  68. this.checkedNodeList.push(node);
  69. } else {
  70. const idx = this.checkedNodeList.indexOf(node);
  71. if (idx !== -1) {
  72. this.checkedNodeList.splice(idx, 1);
  73. }
  74. }
  75. const item = this.list.find(w => w.id === node.origin.id);
  76. onItemSelect(item!);
  77. }
  78. change(ret: TransferChange): void {
  79. const isDisabled = ret.to === 'right';
  80. this.checkedNodeList.forEach(node => {
  81. node.isDisabled = isDisabled;
  82. node.isChecked = isDisabled;
  83. });
  84. }
  85. }

API

nz-transfercomponent

参数说明类型默认值
[nzDataSource]数据源,其中若数据属性 direction: ‘right’ 将会被渲染到右边一栏中或使用 nzTargetKeysTransferItem[][]
[nzDisabled]是否禁用booleanfalse
[nzTitles]标题集合,顺序从左至右string[][‘’, ‘’]
[nzOperations]操作文案集合,顺序从下至上string[][‘’, ‘’]
[nzListStyle]两个穿梭框的自定义样式,等同 ngStyleobject-
[nzItemUnit]单数单位string‘项目’
[nzItemsUnit]复数单位string‘项目’
[nzRenderList]自定义渲染列表,见示例Array<TemplateRef<void> | null>[null, null]
[nzRender]每行数据渲染模板,见示例TemplateRef<void>-
[nzFooter]底部渲染模板,见示例TemplateRef<void>-
[nzShowSearch]是否显示搜索框booleanfalse
[nzFilterOption]接收 inputValueoption 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false(inputValue: string, item: TransferItem) => boolean-
[nzSearchPlaceholder]搜索框的默认值string‘请输入搜索内容’
[nzNotFoundContent]当列表为空时显示的内容string‘列表为空’
[nzCanMove]穿梭时二次校验。注意: 穿梭组件内部始终只保留一份数据,二次校验过程中需取消穿梭项则直接删除该项;具体用法见示例。(arg: TransferCanMove) => Observable<TransferItem[]>-
[nzSelectedKeys]设置被选中的 key 集合string[]-
[nzTargetKeys]显示在右侧框数据的 key 集合string[]-
(nzChange)选项在两栏之间转移时的回调函数EventEmitter<TransferChange>-
(nzSearchChange)搜索框内容时改变时的回调函数EventEmitter<TransferSearchChange>-
(nzSelectChange)选中项发生改变时的回调函数EventEmitter<TransferSearchChange>-

TransferItem

参数说明类型默认值
title标题,用于显示及搜索关键字判断string-
direction指定数据方向,若指定 right 为右栏,其他情况为左栏‘left’ | ‘right’-
disabled指定checkbox为不可用状态booleanfalse
checked指定checkbox为选中状态booleanfalse

TransferCanMove

参数说明类型默认值
direction数据方向‘left’ | ‘right’-
list数据源TransferItem[][]

TransferChange

参数说明类型默认值
from数据方向‘left’ | ‘right’-
to数据方向‘left’ | ‘right’-
list数据源TransferItem[][]

TransferSearchChange

参数说明类型默认值
direction数据方向‘left’ | ‘right’-
value搜索关键词string-

nzRenderList

参数说明类型默认值
direction渲染列表的方向‘left’ | ‘right’-
disabled是否禁用列表boolean-
items过滤后的数据TransferItem[]-
onItemSelect勾选条目(item: TransferItem) => void-
onItemSelectAll勾选一组条目(selected: boolean) => void-