Transfer

Double column transfer choice box.

When To Use#

Transfer the elements between two columns in an intuitive and efficient way.

One or more elements can be selected from either column, one click on the proper 'direction' button, and the transfer is done. The left column is considered the 'source' and the right column is considered the 'target'. As you can see in the API description, these names are reflected in.

Import this Component Individually#

You can get more detail here.

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

Examples

18 itemsSource
2 itemsTarget

The most basic usage of nz-transfer involves providing the source data and target keys arrays, plus the rendering and some callback functions.

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);
  }
}
6 items
14 items

Advanced Usage of Transfer.

You can customize the labels of the transfer buttons, the width and height of the columns, and what should be displayed in the footer.

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) {}
}
9 items
11 items

Custom each Transfer Item, and in this way you can render a complex datasource.

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 items
2 items

Can use nzCanMove to do two-verification.

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 items
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 items
Name
Description
content3description of content3
content4description of content4

Customize render list with Table component.

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 items
  • parent 1
    • leaf 1-1
    • leaf 1-2
0 item
empty

No Data

Customize render list with Tree component.

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#

PropertyDescriptionTypeDefault
[nzDataSource]Used for setting the source data. Except the elements whose keys are direction: 'right' prop , or using nzTargetKeys prop.TransferItem[][]
[nzDisabled]Whether disabled transferbooleanfalse
[nzTitles]A set of titles that are sorted from left to right.string[]['', '']
[nzOperations]A set of operations that are sorted from bottom to top.string[]['', '']
[nzListStyle]A custom CSS style used for rendering the transfer columns. equal ngStyleobject-
[nzItemUnit]single unitstring'item'
[nzItemsUnit]multiple unitstring'items'
[nzRenderList]Customize render list, please refer to the case.Array<TemplateRef<void> | null>[null, null]
[nzRender]The function to generate the item shown on a column. please refer to the case.TemplateRef<void>-
[nzFooter]A function used for rendering the footer. please refer to the case.TemplateRef<void>-
[nzShowSearch]If included, a search box is shown on each column.booleanfalse
[nzFilterOption]A function to determine whether an item should show in search result list(inputValue: string, item: TransferItem) => boolean-
[nzSearchPlaceholder]The hint text of the search box.string'Search here'
[nzNotFoundContent]Text to display when a column is empty.string'The list is empty'
[nzCanMove]Two verification when transfer choice box. please refer to the case.(arg: TransferCanMove) => Observable<TransferItem[]>-
[nzTargetKeys]A set of keys of elements that are listed on the right column.string[]-
(nzChange)A callback function that is executed when the transfer between columns is complete.EventEmitter<TransferChange>-
(nzSearchChange)A callback function which is executed when search field are changedEventEmitter<TransferSearchChange>-
(nzSelectChange)A callback function which is executed when selected items are changed.EventEmitter<TransferSearchChange>-

TransferItem#

PropertyDescriptionTypeDefault
titleUsed to display and search keywordstring-
directionUsed for setting the source data. Except the elements whose keys are direction: 'right' prop.'left' | 'right'-
disabledspecifies whether the checkbox is disabledbooleanfalse
checkedspecifies whether the checkbox is selectedbooleanfalse

TransferCanMove#

PropertyDescriptionTypeDefault
directiondata direction'left' | 'right'-
listUsed for setting the source data.TransferItem[][]

TransferChange#

PropertyDescriptionTypeDefault
fromdata direction'left' | 'right'-
todata direction'left' | 'right'-
listUsed for setting the source data.TransferItem[][]

TransferSearchChange#

PropertyDescriptionTypeDefault
directiondata direction'left' | 'right'-
valueSearch keywordstring-

nzRenderList#

PropertyDescriptionTypeDefault
directionList render direction'left' | 'right'-
disabledDisable list or notboolean-
itemsFiltered itemsTransferItem[]-
onItemSelectSelect item(item: TransferItem) => void-
onItemSelectAllSelect a group of items(selected: boolean) => void-