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 provides an in-depth walkthrough with 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 handles a large share of UI interaction challenges in enterprise applications.