I am using Angular Material's drag-and-drop functionality in my project. While dragging works fine, I am facing issues with dropping the items. The items can be dragged, but when I try to drop them into the drop zone, then the items move to the previous zone and not to the target zone.
What am I doing wrong?
Full example: Demo @ StackBlitz
Here is a simplified version of my code:
task-status-list.component.html
<div cdkDropListGroup class="task-status-list-container">
@for (taskStatus of Object.values(TASK_STATUSES); track taskStatus.key) {
<app-task-status [taskStatus]="taskStatus"></app-task-status>
}
</div>
task-status.component.html
<div class="task-status-container">
<div class="title-container">
<div class="title">{{ taskStatus.value }}</div>
</div>
<div
cdkDropList
[cdkDropListData]="assignTasksByStatus(taskStatus.key)"
(cdkDropListDropped)="drop($event)"
class="task-status-container-body"
>
@for (task of assignTasksByStatus(taskStatus.key);track task.id) {
<app-task cdkDrag [task]="task"></app-task>
}
</div>
</div>
task-status.component.ts
export class TaskStatusComponent {
protected readonly TASK_STATUSES = TASK_STATUSES;
@Input() taskStatus!: TaskStatus;
tasksSubscription!: Subscription;
tasks: Task[] = [
{ id: 1, title: 'Task 1', status: TASK_STATUSES['TO_DO'] },
{ id: 2, title: 'Task 2', status: TASK_STATUSES['TO_DO'] },
{ id: 3, title: 'Task 3', status: TASK_STATUSES['DONE'] },
];
drop(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
);
}
}
public assignTasksByStatus(status: string): Task[] {
return this.tasks.filter((task) => task.status.key === status);
}
}
task.component.html
<div class="title">{{ task.title }}</div>
task.component.ts
export class TaskComponent {
@Input() task!: Task;
}
CdkDropListGroup
in your TaskStatusListComponent
.import { CdkDropListGroup } from '@angular/cdk/drag-drop';
@Component({
selector: 'app-task-status-list',
standalone: true,
imports: [TaskStatusComponent, CdkDropListGroup],
...
})
export class TaskStatusListComponent {
...
}
TaskStatusComponent
, I would suggest that splits the tasks
by status (tasksByStatus
) instead of sharing the data in a single tasks
array. This aims that each CdkDropList
component instance has their own dataset.export class TaskStatusComponent {
...
tasksByStatus!: { [key: string]: Task[] };
ngOnInit() {
this.tasksByStatus = Object.keys(TASK_STATUSES).reduce((acc, cur) => {
acc[cur] = this.tasks.filter((x) => x.status.key === cur);
return acc;
}, {} as { [key: string]: Task[] });
}
public assignTasksByStatus(status: string): Task[] {
return this.tasksByStatus[status];
}
...
}