I'm implementing a drag and drop feature in my Angular application using Angular Material's CDK. The drag and drop functionality works as expected, but I'm struggling with identifying the source and target containers during the drop event.
I have looked into the drop event and checked the event.previousContainer and event.container properties. However, I'm not sure how to use these properties to determine which specific containers (e.g., "TO_DO" or "DONE") an item has been moved between.
How can I modify this to accurately identify the source and target containers? Specifically, I want to determine if a task has been moved from "TO_DO" to "DONE" or vice versa. Is there a way to get the task status as well during the drop event?
Any advice or sample code would be extremely helpful.
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-statust.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'] },
];
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[] });
}
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.tasksByStatus[status];
}
}
task.component.html
<div class="title">{{ task.title }}</div>
task.component.ts
export class TaskComponent {
@Input() task!: Task;
}
You can overwrite the default element id for the CdkDragDrop
component by providing the task status name (TO_DO, Done, etc).
Bonus: If you want to track which item is dropped, you can add [cdkDragData]="task"
attribute.
<div
[id]="taskStatus.key"
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" [cdkDragData]="task"></app-task>
}
</div>
In the drop
method, get the CdkDragDrop
element ids and dropped item as below:
drop(event: CdkDragDrop<Task[]>) {
let fromContainer = event.previousContainer.id;
let toContainer = event.container.id;
let dragDropData: Task = event.item.data;
if (event.previousContainer === event.container) {
moveItemInArray(
event.container.data,
event.previousIndex,
event.currentIndex
);
} else {
console.log(
`Item (${JSON.stringify(
dragDropData
)}) is moved from ${fromContainer} to ${toContainer}.`
);
transferArrayItem(
event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex
);
}
}