Transfer穿梭框

双栏穿梭选择框。

何时使用#

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

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

单独引入此组件#

想要了解更多关于单独引入组件的内容,可以在快速上手页面进行查看。

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

代码演示

18 项Source
2 项Target

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

expand codeexpand code
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'nz-demo-transfer-basic',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      [nzDisabled]="disabled"
      [nzTitles]="['Source', 'Target']"
      (nzSelectChange)="select($event)"
      (nzChange)="change($event)"
    >
    </nz-transfer>
    <div style="margin-top: 8px;">
      <nz-switch [(ngModel)]="disabled" nzCheckedChildren="disabled" nzUnCheckedChildren="disabled"></nz-switch>
      <div></div>
    </div>
  `
})
export class NzDemoTransferBasicComponent implements OnInit {
  // tslint:disable-next-line:no-any
  list: any[] = [];
  disabled = false;

  ngOnInit(): void {
    for (let i = 0; i < 20; i++) {
      this.list.push({
        key: i.toString(),
        title: `content${i + 1}`,
        disabled: i % 3 < 1
      });
    }

    [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  }

  select(ret: {}): void {
    console.log('nzSelectChange', ret);
  }

  change(ret: {}): void {
    console.log('nzChange', ret);
  }
}
13 项
7 项

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

expand codeexpand code
import { Component, OnInit } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd';

@Component({
  selector: 'nz-demo-transfer-advanced',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      nzShowSearch
      [nzOperations]="['to right', 'to left']"
      [nzListStyle]="{ 'width.px': 250, 'height.px': 300 }"
      [nzRender]="render"
      [nzFooter]="footer"
      (nzSelectChange)="select($event)"
      (nzChange)="change($event)"
    >
      <ng-template #render let-item> {{ item.title }}-{{ item.description }} </ng-template>
      <ng-template #footer let-direction>
        <button nz-button (click)="reload(direction)" [nzSize]="'small'" style="float: right; margin: 5px;">
          reload
        </button>
      </ng-template>
    </nz-transfer>
  `
})
export class NzDemoTransferAdvancedComponent implements OnInit {
  list: Array<{ key: string; title: string; description: string; direction: string }> = [];

  ngOnInit(): void {
    this.getData();
  }

  getData(): void {
    const ret: Array<{ key: string; title: string; description: string; direction: string }> = [];
    for (let i = 0; i < 20; i++) {
      ret.push({
        key: i.toString(),
        title: `content${i + 1}`,
        description: `description of content${i + 1}`,
        direction: Math.random() * 2 > 1 ? 'right' : ''
      });
    }
    this.list = ret;
  }

  reload(direction: string): void {
    this.getData();
    this.msg.success(`your clicked ${direction}!`);
  }

  select(ret: {}): void {
    console.log('nzSelectChange', ret);
  }

  change(ret: {}): void {
    console.log('nzChange', ret);
  }

  constructor(public msg: NzMessageService) {}
}
11 项
9 项

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

expand codeexpand code
import { Component, OnInit } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd';

@Component({
  selector: 'nz-demo-transfer-custom-item',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      [nzListStyle]="{ 'width.px': 300, 'height.px': 300 }"
      [nzRender]="render"
      (nzSelectChange)="select($event)"
      (nzChange)="change($event)"
    >
      <ng-template #render let-item> <i nz-icon nzType="{{ item.icon }}"></i> {{ item.title }} </ng-template>
    </nz-transfer>
  `
})
export class NzDemoTransferCustomItemComponent implements OnInit {
  list: Array<{ key: string; title: string; description: string; direction: string; icon: string }> = [];

  ngOnInit(): void {
    this.getData();
  }

  getData(): void {
    const ret: Array<{ key: string; title: string; description: string; direction: string; icon: string }> = [];
    for (let i = 0; i < 20; i++) {
      ret.push({
        key: i.toString(),
        title: `content${i + 1}`,
        description: `description of content${i + 1}`,
        direction: Math.random() * 2 > 1 ? 'right' : '',
        icon: `frown-o`
      });
    }
    this.list = ret;
  }

  select(ret: {}): void {
    console.log('nzSelectChange', ret);
  }

  change(ret: {}): void {
    console.log('nzChange', ret);
  }

  constructor(public msg: NzMessageService) {}
}
18 项
2 项

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

expand codeexpand code
import { Component, OnInit } from '@angular/core';
import { TransferCanMove, TransferItem } from 'ng-zorro-antd';
import { of, Observable } from 'rxjs';
import { delay } from 'rxjs/operators';

@Component({
  selector: 'nz-demo-transfer-can-move',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      [nzCanMove]="canMove"
      (nzSelectChange)="select($event)"
      (nzChange)="change($event)"
    >
    </nz-transfer>
  `
})
export class NzDemoTransferCanMoveComponent implements OnInit {
  list: Array<{ key: string; title: string; disabled: boolean; direction?: string }> = [];

  ngOnInit(): void {
    for (let i = 0; i < 20; i++) {
      this.list.push({
        key: i.toString(),
        title: `content${i + 1}`,
        disabled: i % 3 < 1
      });
    }

    [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  }

  canMove(arg: TransferCanMove): Observable<TransferItem[]> {
    if (arg.direction === 'right' && arg.list.length > 0) {
      arg.list.splice(0, 1);
    }
    // or
    // if (arg.direction === 'right' && arg.list.length > 0) delete arg.list[0];
    return of(arg.list).pipe(delay(1000));
  }

  select(ret: {}): void {
    console.log('nzSelectChange', ret);
  }

  change(ret: {}): void {
    console.log('nzChange', ret);
  }
}
18 项
Name
Tag
Description
content1catdescription of content1
content2dogdescription of content2
content5dogdescription of content5
content6birddescription of content6
content7catdescription of content7
content8dogdescription of content8
content9birddescription of content9
content10catdescription of content10
content11dogdescription of content11
content12birddescription of content12
2 项
Name
Description
content3description of content3
content4description of content4

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

expand codeexpand code
import { Component, OnInit } from '@angular/core';
import { TransferItem } from 'ng-zorro-antd/transfer';

@Component({
  selector: 'nz-demo-transfer-table-transfer',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      [nzDisabled]="disabled"
      [nzShowSearch]="showSearch"
      [nzShowSelectAll]="false"
      [nzRenderList]="[renderList, renderList]"
      (nzSelectChange)="select($event)"
      (nzChange)="change($event)"
    >
      <ng-template
        #renderList
        let-items
        let-direction="direction"
        let-stat="stat"
        let-disabled="disabled"
        let-onItemSelectAll="onItemSelectAll"
        let-onItemSelect="onItemSelect"
      >
        <nz-table #t [nzData]="convertItems(items)" nzSize="small">
          <thead>
            <tr>
              <th
                nzShowCheckbox
                [nzDisabled]="disabled"
                [nzChecked]="stat.checkAll"
                [nzIndeterminate]="stat.checkHalf"
                (nzCheckedChange)="onItemSelectAll($event)"
              ></th>
              <th>Name</th>
              <th *ngIf="direction === 'left'">Tag</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr *ngFor="let data of t.data" (click)="onItemSelect(data)">
              <td
                nzShowCheckbox
                [nzChecked]="data.checked"
                [nzDisabled]="disabled || data.disabled"
                (nzCheckedChange)="onItemSelect(data)"
              ></td>
              <td>{{ data.title }}</td>
              <td *ngIf="direction === 'left'">
                <nz-tag>{{ data.tag }}</nz-tag>
              </td>
              <td>{{ data.description }}</td>
            </tr>
          </tbody>
        </nz-table>
      </ng-template>
    </nz-transfer>
    <div style="margin-top: 8px;">
      <nz-switch [(ngModel)]="disabled" nzCheckedChildren="disabled" nzUnCheckedChildren="disabled"></nz-switch>
      <nz-switch [(ngModel)]="showSearch" nzCheckedChildren="showSearch" nzUnCheckedChildren="showSearch"></nz-switch>
    </div>
  `
})
export class NzDemoTransferTableTransferComponent implements OnInit {
  // tslint:disable-next-line:no-any
  list: any[] = [];
  disabled = false;
  showSearch = false;

  ngOnInit(): void {
    for (let i = 0; i < 20; i++) {
      this.list.push({
        key: i.toString(),
        title: `content${i + 1}`,
        description: `description of content${i + 1}`,
        disabled: i % 4 === 0,
        tag: ['cat', 'dog', 'bird'][i % 3]
      });
    }

    [2, 3].forEach(idx => (this.list[idx].direction = 'right'));
  }

  convertItems(items: TransferItem[]): TransferItem[] {
    return items.filter(i => !i.hide);
  }

  select(ret: {}): void {
    console.log('nzSelectChange', ret);
  }

  change(ret: {}): void {
    console.log('nzChange', ret);
  }
}
3 项
  • parent 1
    • leaf 1-1
    • leaf 1-2
0 项
empty

暂无数据

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

expand codeexpand code
// tslint:disable: no-any
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { NzTreeNode } from 'ng-zorro-antd/core';
import { TransferChange, TransferItem } from 'ng-zorro-antd/transfer';
import { NzTreeComponent } from 'ng-zorro-antd/tree';

@Component({
  selector: 'nz-demo-transfer-tree-transfer',
  template: `
    <nz-transfer
      [nzDataSource]="list"
      [nzShowSelectAll]="false"
      [nzRenderList]="[leftRenderList, null]"
      (nzChange)="change($event)"
    >
      <ng-template #leftRenderList let-items let-onItemSelectAll="onItemSelectAll" let-onItemSelect="onItemSelect">
        <nz-tree #tree [nzData]="treeData" nzExpandAll nzBlockNode>
          <ng-template #nzTreeTemplate let-node>
            <span
              class="ant-tree-checkbox"
              [class.ant-tree-checkbox-disabled]="node.isDisabled"
              [class.ant-tree-checkbox-checked]="node.isChecked"
              (click)="checkBoxChange(node, onItemSelect)"
            >
              <span class="ant-tree-checkbox-inner"></span>
            </span>
            <span
              (click)="checkBoxChange(node, onItemSelect)"
              class="ant-tree-node-content-wrapper ant-tree-node-content-wrapper-open"
              >{{ node.title }}</span
            >
          </ng-template>
        </nz-tree>
      </ng-template>
    </nz-transfer>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NzDemoTransferTreeTransferComponent {
  @ViewChild('tree', { static: true }) tree: NzTreeComponent;
  list: any[] = [
    { id: 1, parentid: 0, title: 'parent 1' },
    { id: 2, parentid: 1, title: 'leaf 1-1', disabled: true, isLeaf: true },
    { id: 3, parentid: 1, title: 'leaf 1-2', isLeaf: true }
  ];
  treeData = this.generateTree(this.list);
  checkedNodeList: NzTreeNode[] = [];

  private generateTree(arr: TransferItem[]): TransferItem[] {
    const tree: TransferItem[] = [];
    const mappedArr: any = {};
    let arrElem: TransferItem;
    let mappedElem: TransferItem;

    for (let i = 0, len = arr.length; i < len; i++) {
      arrElem = arr[i];
      mappedArr[arrElem.id] = { ...arrElem };
      mappedArr[arrElem.id].children = [];
    }

    for (const id in mappedArr) {
      if (mappedArr.hasOwnProperty(id)) {
        mappedElem = mappedArr[id];
        if (mappedElem.parentid) {
          mappedArr[mappedElem.parentid].children.push(mappedElem);
        } else {
          tree.push(mappedElem);
        }
      }
    }
    return tree;
  }

  checkBoxChange(node: NzTreeNode, onItemSelect: (item: TransferItem) => void): void {
    if (node.isDisabled) {
      return;
    }
    node.isChecked = !node.isChecked;
    if (node.isChecked) {
      this.checkedNodeList.push(node);
    } else {
      const idx = this.checkedNodeList.indexOf(node);
      if (idx !== -1) {
        this.checkedNodeList.splice(idx, 1);
      }
    }
    const item = this.list.find(w => w.id === node.origin.id);
    onItemSelect(item);
  }

  change(ret: TransferChange): void {
    const isDisabled = ret.to === 'right';
    this.checkedNodeList.forEach(node => {
      node.isDisabled = isDisabled;
      node.isChecked = isDisabled;
    });
  }
}

API#

nz-transfer#

参数说明类型默认值
[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[]>-
[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-