@angular/cdk/drag-drop模塊可以幫助我們在水平/垂直列表中拖動單個項目或對其進行排序,在列表之間傳遞項目,顯示動畫、預覽、占位符、自定義拖放句柄。在本教程中,我們將創建許多簡單的例子來展示如何使用Angular 7 Material CDK – Drag and Drop。 ...
完整代碼在最後,下麵講解以此代碼為例
1.環境配置
1.1 安裝@angular/material、@angular/cdk
cnpm install --save @angular/material @angular/cdk
1.2 app.modules.ts導入
import { DragDropModule } from '@angular/cdk/drag-drop';
imports: [
...
DragDropModule
]
2.功能實現
2.1 拖拽
html編輯如下代碼即可:
<div cdkDrag class="drag-box" drag me </div>
2.2 排序
html:
<h3>列表排序</h3> <div class="box-list" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="drag-box" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div> </div>
ts:
import { ...CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; customers = [ { name: 'Adam', age: 23 }, { name: 'Jack', age: 27 }, { name: 'Katherin', age: 26 }, { name: 'John', age: 30 }, { name: 'Watson', age: 42 }, ]; drop(event: CdkDragDrop<string[]>) { console.log('列表排序:', event, this.customers); moveItemInArray(this.customers, event.previousIndex, event.currentIndex); }
執行如下操作:
列印結果:
2.3 橫向排序
html:
<h3>橫向排序</h3> <div class="box-list-horizontal" cdkDropList cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)"> <div class="drag-box" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div> </div>
2.4 添加動畫效果
為了設置動畫,我們定義了一個以transform屬性為目標的轉換。拖放CDK支持動畫:
--對列表中的元素進行拖動排序時的動畫=> .cdk-drag
.cdk-drag { transition: transform 100ms ease; }
--從動畫的位置到最終把它放在列表的位置上時的動畫=> .cdk-drag-animating
.cdk-drag-animating { transition: transform 300ms ease; }
2.5 占位符元素 placeholder
預設占位符
使用.cdk-drag-placeholder來顯示占位符元素,而不是實際的元素,因為它是在cdkDropList中拖動的。預設情況下,這看起來與正在排序的元素完全相同。
.cdk-drag-placeholder { background: #ccc; border: dotted 1px #999; transition: transform 500ms ease; }
拖拽時效果如下圖:
自定義占位符
使用*cdkDragPlaceholder指令,我們可以用一個自定義的占位符來代替預設的占位符
html:
<div class="box-list customph" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag > <div class="box-custom-placeholder" *cdkDragPlaceholder></div> {{customer.name}} </div> </div>
css:
// 自定義占位符 .customph{ .box-custom-placeholder { height: 28px; width: 100px; background: #fff; border: dotted 1px #0084ff; transition: transform 200ms ease; } &.box-list.cdk-drop-list-dragging .drag-box:not(.cdk-drag-placeholder) { transition: transform 500ms ease; } }
2.6 預覽
當提取並拖動cdkDrag元素時,可以看到預覽元素。預設情況下,預覽元素看起來與被拖動的元素完全相同。
使用.cdk-drag-preview來定義預覽CSS:
// 預覽 .cdk-drag-preview { height: 28px; width: 100px; box-shadow: 0 3px 3px -3px #0084ff; }
我們還需要使用*cdkDragPreview提供一個自定義模板:
<div cdkDropList class="box-list" (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag> {{customer.name}} <p *cdkDragPreview>Age: {{customer.age}}</p> </div> </div>
2.7 多個列表之間拖拽
我們可以使用cdkDropListConnectedTo屬性將一個或多個cdkDropList連接到一起。然後設置cdkDropListData和cdkDragData,將數據與cdkDropList和cdkDrag關聯起來。
html:
<div class="box-list" cdkDropList #inactiveList="cdkDropList" id="Inactive Customers" [cdkDropListData]="inactiveCustomers" [cdkDropListConnectedTo]="[activeList]" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of inactiveCustomers" cdkDrag> {{customer.name}} </div> </div> <div class="box-list" cdkDropList #activeList="cdkDropList" id="Active Customers" [cdkDropListData]="activeCustomers" [cdkDropListConnectedTo]="[inactiveList]" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div>
ts:
import { ...transferArrayItem } from '@angular/cdk/drag-drop'; inactiveCustomers = [ {name: 'Jack', age: 18}, {name: 'Katherin', age: 16}, {name: 'Adam', age: 36} ]; activeCustomers = [ {name: 'John', age: 10}, {name: 'Watson', age: 24} ]; drop1(event: CdkDragDrop<string[]>) { console.log('拖拽事件:event', event); if (event.previousContainer === event.container) { console.log('拖拽事件', `> 拖 '${event.item.data}' 到 '${event.container.id}'`); moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); } else { console.log('拖拽事件', `> 拖 '${event.item.data}' 到 '${event.container.id}'`); transferArrayItem( event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex); } }
執行如下操作:
列印結果:
CdkDropListGroup
CdkDropListGroup指令的範圍比CdkDropList要大一級。CdkDropListGroup可以包含多個CdkDropList。而且當CdkDropListGroup包含多個CdkDropList的時候,這些CdkDropList直接是相互connect的(CdkDropList就不用去寫cdkDropListConnectedTo屬性了)。
<div cdkDropListGroup> <div class="box-list" cdkDropList id="Inactive Customers" [cdkDropListData]="inactiveCustomers" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of inactiveCustomers" cdkDrag> {{customer.name}} </div> </div> <div class="box-list" cdkDropList id="Active Customers" [cdkDropListData]="activeCustomers" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div> </div>
2.8 事件監聽
Drag Event Handlers
– cdkDragStarted: 當用戶開始拖動時發出。
– cdkDragEnded: 當用戶停止拖動時發出。
– cdkDragEntered: 將item移動到新容器中時發出。
– cdkDragExited: 將item移出當前容器時發出。
Drop Event Handlers
– cdkDropListDropped: emits when an item was dropped inside the container.
– cdkDropListEntered: emits when a new item is dragged into this container.
– cdkDropListExited: emits when an item is dragged from this container into another container.
cdkDragStarted、cdkDragEnded、cdkDragMoved
html:
<h3>事件監聽</h3> <div cdkDrag class="drag-box" (cdkDragStarted)="dragStarted($event)" (cdkDragEnded)="dragEnded($event)" (cdkDragMoved)="dragMoved($event)" > drag me </div> <p>{{state}} {{position}}</p>
ts:
import { CdkDragEnd, CdkDragStart, CdkDragMove... } from '@angular/cdk/drag-drop'; dragStarted(event: CdkDragStart) { this.state = '拖拽開始'; console.log('拖拽開始'); } dragEnded(event: CdkDragEnd) { this.state = '拖拽結束'; console.log('拖拽結束'); } dragMoved(event: CdkDragMove) { console.log('dragMoved'); this.position = `> Position X: ${event.pointerPosition.x} - Y: ${event.pointerPosition.y}`; }
cdkDropListEntered、cdkDropListExited、cdkDragEntered、cdkDragExited
html:
<div class="box-list" cdkDropList #inactiveList="cdkDropList" id="Inactive Customers" [cdkDropListData]="inactiveCustomers" [cdkDropListConnectedTo]="[activeList]" (cdkDropListDropped)="drop1($event)" (cdkDropListEntered)="dropListEntered($event)" (cdkDropListExited)="dropListExited($event)"> <div class="drag-box dragList" [cdkDragData]="customer" (cdkDragEntered)="dragEntered($event)" (cdkDragExited)="dragExited($event)" *ngFor="let customer of inactiveCustomers" cdkDrag> {{customer}} </div> </div> <div class="box-list" cdkDropList #activeList="cdkDropList" id="Active Customers" [cdkDropListData]="activeCustomers" [cdkDropListConnectedTo]="[inactiveList]" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer}} </div> </div>
ts:
import { ...CdkDragEnter, CdkDragExit} from '@angular/cdk/drag-drop'; dragEntered(event: CdkDragEnter) { console.log(`drag:把 '${event.item.data}' 拖進 '${event.container.id}' `); } dragExited(event: CdkDragExit) { console.log(`drag:把 '${event.item.data}' 從 '${event.container.id}' 中拖出 `); } dropListEntered(event: CdkDragEnter) { console.log(`drop:'${event.container.id}' 中拖進了 '${event.item.data}'`); } dropListExited(event: CdkDragExit) { console.log(`drop:從 '${event.container.id}' 中拖出 '${event.item.data}'`); }
執行如下操作
列印結果:
再執行如下操作:
列印結果:
cdkDropListSorted
拖拽過程中交換item的時候回調
<div class="box-list" cdkDropList #activeList="cdkDropList" id="Active Customers" [cdkDropListData]="activeCustomers" [cdkDropListConnectedTo]="[inactiveList]" (cdkDropListDropped)="drop1($event)" (cdkDropListSorted)="dropListSorted($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div>
ts:
dropListSorted(event: CdkDropList) { console.log('Sorted:', event); }
2.9 屬性
cdkDropListEnterPredicate
enterPredicate: (drag: CdkDrag, drop: CdkDropList) => boolean
指定哪些item是可以拖拽到當前容器
html:
<div class="box-list" cdkDropList #activeList="cdkDropList" id="Active Customers" [cdkDropListData]="activeCustomers" [cdkDropListConnectedTo]="[inactiveList]" (cdkDropListDropped)="drop1($event)" [cdkDropListEnterPredicate]="predicate"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div>
ts:
import { ...CdkDrag, CdkDropList } from '@angular/cdk/drag-drop'; predicate(drag: CdkDrag, drop: CdkDropList) { console.log('predicate:', drag, drop); return drag.data.age >= 18; }
CdkDrag屬性:
CdkDropList屬性
代碼:
html:
<div class="drag-box" cdkDrag> drag me </div> <h3>鎖定方向</h3> <div class="drag-box" cdkDragLockAxis="y" cdkDrag> only up/down </div> <div class="drag-box" cdkDragLockAxis="x" cdkDrag> only left/right </div> <h3>事件監聽</h3> <div cdkDrag class="drag-box" (cdkDragStarted)="dragStarted($event)" (cdkDragEnded)="dragEnded($event)" (cdkDragMoved)="dragMoved($event)" > drag me </div> <p>{{state}} {{position}}</p> <h3>拖拽handler</h3> <div class="drag-box-with-handler drag-box" cdkDrag> <div class="box-handler" cdkDragHandle> 點我拖拽 </div> </div> <h3>列表排序</h3> <div class="box-list" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div> </div> <h3>橫向排序</h3> <div class="box-list-horizontal" cdkDropList cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)"> <div class="drag-box dragListH" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div> </div> <h3>placeholder 預設占位符元素</h3> <div>使用.cdk-drag-placeholder來顯示占位符元素,而不是實際的元素, 因為它是在cdkDropList中拖動的。預設情況下,占位符元素看起來與正在排序的元素完全相同。</div> <div class="box-list defplaceh" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div> </div> <h3>placeholder 自定義占位符元素</h3> <div>使用*cdkDragPlaceholder指令,我們可以用一個自定義的占位符來代替預設的占位符</div> <div class="box-list customph" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag > <div class="box-custom-placeholder" *cdkDragPlaceholder></div> {{customer.name}} </div> </div> <h3>預覽 Preview</h3> <div>當提取並拖動cdkDrag元素時,可以看到預覽元素。預設情況下, 預覽元素看起來與被拖動的元素完全相同。</div> <div cdkDropList class="box-list" (cdkDropListDropped)="drop($event)"> <div class="drag-box dragList" *ngFor="let customer of customers" cdkDrag> {{customer.name}} <p *cdkDragPreview>Age: {{customer.age}}</p> </div> </div> <h3>兩個列表間拖放</h3> <div class="twoList"> <div class="box-list" cdkDropList #inactiveList="cdkDropList" id = "Inactive Customers" [cdkDropListData]="inactiveCustomers" [cdkDropListConnectedTo]="[activeList]" (cdkDropListDropped)="drop1($event)" (cdkDropListEntered)="dropListEntered($event)" (cdkDropListExited)="dropListExited($event)" [cdkDropListEnterPredicate]="predicate" > <div class="drag-box dragList" [cdkDragData]="customer" (cdkDragEntered)="dragEntered($event)" (cdkDragExited)="dragExited($event)" *ngFor="let customer of inactiveCustomers;isLast as last" cdkDrag> {{customer.name}} </div> </div> <div class="box-list" cdkDropList #activeList="cdkDropList" id = "Active Customers" [cdkDropListData]="activeCustomers" [cdkDropListConnectedTo]="[inactiveList]" (cdkDropListDropped)="drop1($event)" [cdkDropListEnterPredicate]="predicate" (cdkDropListSorted)="dropListSorted($event)" > <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div> </div> <!-- <h3>兩個列表間拖放 cdkDropListGroup</h3> <div class="twoList" cdkDropListGroup> <div class="box-list" cdkDropList id = "Inactive Customers" [cdkDropListData]="inactiveCustomers" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of inactiveCustomers" cdkDrag> {{customer.name}} </div> </div> <div class="box-list" cdkDropList id = "Active Customers" [cdkDropListData]="activeCustomers" (cdkDropListDropped)="drop1($event)"> <div class="drag-box dragList" [cdkDragData]="customer" *ngFor="let customer of activeCustomers" cdkDrag> {{customer.name}} </div> </div> </div> -->
ts:
import { Component, OnInit } from '@angular/core'; import { CdkDragEnd, CdkDragStart, CdkDragMove, CdkDragDrop, CdkDragEnter, CdkDragExit, moveItemInArray, transferArrayItem, CdkDrag, CdkDropList } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-drag-drop', templateUrl: './drag-drop.component.html', styleUrls: ['./drag-drop.component.scss'] }) export class DragDropComponent implements OnInit { state = ''; position = ''; customers = [ { name: 'Adam', age: 23 }, { name: 'Jack', age: 27 }, { name: 'Katherin', age: 26 }, { name: 'John', age: 30 }, { name: 'Watson', age: 42 }, ]; inactiveCustomers = [ {name: 'Jack', age: 18}, {name: 'Katherin', age: 16}, {name: 'Adam', age: 36} ]; activeCustomers = [ {name: 'John', age: 10}, {name: 'Watson', age: 24} ]; constructor() { } ngOnInit() { } dragStarted(event: CdkDragStart) { this.state = '拖拽開始'; console.log('拖拽開始'); } dragEnded(event: CdkDragEnd) { this.state = '拖拽結束'; console.log('拖拽結束'); } dragMoved(event: CdkDragMove) { console.log('dragMoved'); this.position = `> Position X: ${event.pointerPosition.x} - Y: ${event.pointerPosition.y}`; } dragEntered(event: CdkDragEnter) { console.log(`drag:把 '${event.item.data}' 拖進 '${event.container.id}' `); } dragExited(event: CdkDragExit) { console.log(`drag:把 '${event.item.data}' 從 '${event.container.id}' 中拖出 `); } dropListEntered(event: CdkDragEnter) { console.log(`drop:'${event.container.id}' 中拖進了 '${event.item.data}'`); } dropListExited(event: CdkDragExit) { console.log(`drop:從 '${event.container.id}' 中拖出 '${event.item.data}'`); } drop(event: CdkDragDrop<string[]>) { console.log('列表排序:', event, this.customers); moveItemInArray(this.customers, event.previousIndex, event.currentIndex); } drop1(event: CdkDragDrop<string[]>) { console.log('拖拽事件:event', event); if (event.previousContainer === event.container) { console.log('拖拽事件', `> 拖 '${event.item.data}' 到 '${event.container.id}'`); moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); } else { console.log('拖拽事件', `> 拖 '${event.item.data}' 到 '${event.container.id}'`); transferArrayItem( event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex); } } predicate(drag: CdkDrag, drop: CdkDropList) { console.log('predicate:', drag, drop); return drag.data.age >= 18; } dropListSorted(event: CdkDropList) { console.log('Sorted:', event); } }
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { MatCardModule } from '@angular/material'; import { DragDropModule } from '@angular/cdk/drag-drop'; // import { VIEWPORT_RULER_PROVIDER } from '@angular/cdk/scrolling'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component'; import { DragDropComponent } from './drag-drop/drag-drop.component'; @NgModule({ declarations: [ AppComponent, VirtualScrollComponent, DragDropComponent ], imports: [ BrowserModule, AppRoutingModule, ScrollingModule, MatCardModule, DragDropModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
css:
.drag-box{ height: 100px; width: 100px; border: 1px solid #000; } .box-handler{ width: 100%; height: 20px; background: #ccc; } .dragList{ height: 28px; margin: 5px 0; } .dragListH{ display: inline-block; margin: 0 5px; height: 28px; width: 55px; } // 動畫效果 // .cdk-drag { // transition: transform 100ms ease; // } .cdk-drag-animating { transition: transform 300ms ease; } // 占位符 預設 placeholder .defplaceh .cdk-drag-placeholder { background: #ccc; border: dotted 1px #999; transition: transform 500ms ease; } // 自定義占位符 .customph{ .box-custom-placeholder { height: 28px; width: 100px; background: #fff; border: dotted 1px #0084ff; transition: transform 200ms ease; } &.box-list.cdk-drop-list-dragging .drag-box:not(.cdk-drag-placeholder) { transition: transform 500ms ease; } } // 預覽 .cdk-drag-preview { height: 28px; width: 100px; box-shadow: 0 3px 3px -3px #0084ff; } .twoList{ overflow: hidden; >div{ float: left; margin: 10px; } }