After the official release of Angular 7, the CDK's Drag and Drop and Virtual Scrolling features quickly became the most popular additions. This article walks through both features using real project examples.
Drag-and-Drop Sorting: Task Board
Installation
npm install @angular/cdk
Single-column Drag Sorting
{% raw %}
import { DragDropModule } from "@angular/cdk/drag-drop";
@Component({
selector: "app-sortable-list",
template: `
<div cdkDropList class="task-list" (cdkDropListDropped)="drop($event)">
<div *ngFor="let task of tasks" cdkDrag class="task-item">
<mat-icon cdkDragHandle>drag_handle</mat-icon>
<span>{{ task.title }}</span>
<span class="badge" [ngClass]="task.priority">{{ task.priority }}</span>
</div>
</div>
`,
})
export class SortableListComponent {
tasks: Task[] = [
{ id: 1, title: "Requirements Analysis", priority: "high" },
{ id: 2, title: "UI Design", priority: "medium" },
{ id: 3, title: "Frontend Development", priority: "high" },
{ id: 4, title: "API Integration", priority: "medium" },
{ id: 5, title: "Testing & Deployment", priority: "low" },
];
drop(event: CdkDragDrop<Task[]>) {
moveItemInArray(this.tasks, event.previousIndex, event.currentIndex);
this.saveOrder(); // persist the new order
}
saveOrder() {
const order = this.tasks.map((t, i) => ({ id: t.id, sort: i }));
this.taskService.updateOrder(order).subscribe();
}
}
{% endraw %}
Multi-column Kanban Board (Cross-column Dragging)
{% raw %}
<div class="kanban-board">
<div
*ngFor="let column of columns"
cdkDropList
[cdkDropListData]="column.tasks"
[cdkDropListConnectedTo]="getConnectedLists(column)"
(cdkDropListDropped)="onDrop($event)"
>
<h3>{{ column.title }}</h3>
<div *ngFor="let task of column.tasks" cdkDrag>{{ task.title }}</div>
</div>
</div>
{% endraw %}
onDrop(event: CdkDragDrop<Task[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
transferArrayItem(
event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex
);
// Update task status
const task = event.container.data[event.currentIndex];
const newStatus = this.getColumnStatus(event.container.id);
this.taskService.updateStatus(task.id, newStatus).subscribe();
}
}
Virtual Scrolling: Large Data Lists
{% raw %}
@Component({
template: `
<cdk-virtual-scroll-viewport
itemSize="60"
style="height: 500px; overflow-y: auto"
>
<mat-list-item
*cdkVirtualFor="let item of items; let i = index"
class="list-item"
>
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{ item.name }}</h4>
<p mat-line>{{ item.email }}</p>
</mat-list-item>
</cdk-virtual-scroll-viewport>
`,
})
export class VirtualUserListComponent {
// Smooth even with 100,000 items
items: User[] = generateLargeDataset(100000);
}
{% endraw %}
Key parameters:
itemSize: item height in px — best performance with fixed height- For variable-height items, use
AutoSizeVirtualScrollStrategy(experimental)
Combining Drag-and-Drop with Virtual Scrolling
There is one limitation when combining the two: the DOM nodes generated by cdkVirtualFor are dynamic, and directly combining them with cdkDrag causes offset calculation issues. Recommendation:
- < 500 items: use
*ngFor+cdkDragdirectly 500 items: use pagination instead of mixing virtual scrolling with drag-and-drop
Summary
The Angular CDK Drag and Drop module is very well designed — Kanban-style applications require almost no additional code. Virtual scrolling is the go-to solution for large data lists. Mastering these two APIs lets you tackle a large share of the UI interaction challenges common in enterprise applications.
