Using angular CDK Drag and Drop (v17x) you can control if a drop is allowed using cdkDropListEnterPredicate. Works fine. But how do I make that visible on screen? Change the cursor or change a color, anything to inform the user if dropping there will be allowed. Is there any style set or any property?
Here is an approach using cursor: no-drop
.
First we store the currently dragging element number in a property, when the drag starts, for this we use the event (cdkDragStarted)="selectedDrag(number)"
.
<div
class="example-box"
[cdkDragData]="number"
cdkDrag
(cdkDragStarted)="selectedDrag(number)"
>
{{number}}
</div>
After this, we simply need to apply the class to the drop elements, because the cursor that get's displayed is not the dragging elements cursor, but the elements below it. So we add the class using ngClass
to the dropping elements as well as the container of the elements (to handle no elements in container scenario).
<div class="example-container">
<h2>Even numbers</h2>
<div
id="even"
cdkDropList
[cdkDropListData]="even"
cdkDropListConnectedTo="all"
class="example-list"
[ngClass]="{'is-invalid': num && num % 2 !== 0 }"
[cdkDropListEnterPredicate]="evenPredicate"
>
@for (number of even; track number) {
<div
class="example-box"
cdkDrag
[cdkDragData]="number"
[ngClass]="{'is-invalid': num && num % 2 !== 0 }"
>
{{number}}
</div>
}
</div>
</div>
Finally we add the CSS to the global styles to show the no--drop
cursor.
.cdk-drag.example-box.is-invalid {
cursor: no-drop !important;
}
.is-invalid {
cursor: no-drop !important;
}
<div class="example-container">
<h2>Available numbers</h2>
<div
id="all"
cdkDropList
[cdkDropListData]="all"
cdkDropListConnectedTo="even"
class="example-list"
(cdkDropListDropped)="drop($event)"
[cdkDropListEnterPredicate]="noReturnPredicate"
>
@for (number of all; track number) {
<div
class="example-box"
[cdkDragData]="number"
cdkDrag
(cdkDragStarted)="selectedDrag(number)"
>
{{number}}
</div>
}
</div>
</div>
{{showCursor | json}}
<div class="example-container">
<h2>Even numbers</h2>
<div
id="even"
cdkDropList
[cdkDropListData]="even"
cdkDropListConnectedTo="all"
class="example-list"
[ngClass]="{'is-invalid': num && num % 2 !== 0 }"
[cdkDropListEnterPredicate]="evenPredicate"
>
@for (number of even; track number) {
<div
class="example-box"
cdkDrag
[cdkDragData]="number"
[ngClass]="{'is-invalid': num && num % 2 !== 0 }"
>
{{number}}
</div>
}
</div>
</div>
import { Component } from '@angular/core';
import {
CdkDragDrop,
moveItemInArray,
transferArrayItem,
CdkDrag,
CdkDropList,
} from '@angular/cdk/drag-drop';
import { CommonModule } from '@angular/common';
/**
* @title Drag&Drop enter predicate
*/
@Component({
selector: 'cdk-drag-drop-enter-predicate-example',
templateUrl: 'cdk-drag-drop-enter-predicate-example.html',
styleUrl: 'cdk-drag-drop-enter-predicate-example.css',
standalone: true,
imports: [CdkDropList, CdkDrag, CommonModule],
})
export class CdkDragDropEnterPredicateExample {
showCursor = false;
all = [1, 2, 3, 4, 5, 6, 7, 8, 9];
even = [10];
num: number | null = null;
selectedDrag(num: number) {
this.num = num;
}
drop(event: CdkDragDrop<number[]>) {
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
);
}
}
/** Predicate function that only allows even numbers to be dropped into a list. */
evenPredicate(item: CdkDrag<number>) {
return item.data % 2 === 0;
}
/** Predicate function that doesn't allow items to be dropped into a list. */
noReturnPredicate() {
return true;
}
enter() {
console.log('enter');
}
leave() {
console.log('leave');
}
}