The Problem
The Angular CDK drag-drop module provides powerful drag and drop functionality. However, items can escape their boundary containers, and scrolling behavior during drag operations can be broken, causing poor user experience.
Symptoms
- Draggable items can be dragged outside their container
- List does not auto-scroll when dragging near edges
- Drop position is incorrect after scrolling
- Items flicker or jump during drag
Real Error Scenario
<div class="scrollable-list">
<div
cdkDrag
*ngFor="let item of items; trackBy: trackByFn"
[cdkDragData]="item">
{{ item.name }}
</div>
</div>Without proper configuration, items can be dragged outside the scrollable container, and the list does not auto-scroll.
How to Fix It
Fix 1: Constrain to Parent Container
<div class="scrollable-list" cdkDropList>
<div
cdkDrag
[cdkDragBoundary]="'cdk-drop-list'"
*ngFor="let item of items"
[cdkDragData]="item">
{{ item.name }}
</div>
</div>.scrollable-list {
max-height: 400px;
overflow-y: auto;
position: relative;
}Fix 2: Enable Auto-Scroll
<div
cdkDropList
cdkDropListAutoScroll
[cdkDropListAutoScrollStep]="4"
(cdkDropListDropped)="onDrop($event)">
<div cdkDrag *ngFor="let item of items">
{{ item.name }}
</div>
</div>Fix 3: Constrain Drag with cdkDragConstrainPosition
constrainPosition(dragRef, point) {
const boundary = dragRef.element.nativeElement.parentElement.getBoundingClientRect();
return {
x: Math.max(boundary.left, Math.min(point.x, boundary.right)),
y: Math.max(boundary.top, Math.min(point.y, boundary.bottom))
};
}<div
cdkDrag
[cdkDragConstrainPosition]="constrainPosition">
{{ item.name }}
</div>Fix 4: Handle Scroll Position During Drop
```typescript onDrop(event: CdkDragDrop<string[]>) { // Account for scroll position const scrollTop = this.scrollContainer.nativeElement.scrollTop;
moveItemInArray(this.items, event.previousIndex, event.currentIndex);
// Restore scroll position if needed this.scrollContainer.nativeElement.scrollTop = scrollTop; } ```
Fix 5: Virtual Scroll Integration
<cdk-virtual-scroll-viewport [itemSize]="50" class="scroll-viewport">
<div cdkDropList (cdkDropListDropped)="onDrop($event)">
<div
cdkDrag
*cdkVirtualFor="let item of items"
[cdkDragData]="item">
{{ item.name }}
</div>
</div>
</cdk-virtual-scroll-viewport>