Skip to content

Commit e335357

Browse files
authored
fix(cdk/drag-drop): receiving class not updated in OnPush component (#26386)
Fixes that the `cdk-drop-list-receiving` class wasn't being updated when two lists are connected across components boundaries that are using OnPush change detection. Fixes #26362.
1 parent 14919bd commit e335357

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5838,6 +5838,33 @@ describe('CdkDrag', () => {
58385838
}),
58395839
);
58405840

5841+
it('should set the receiving class when the list is wrapped in an OnPush component', fakeAsync(() => {
5842+
const fixture = createComponent(ConnectedDropListsInOnPush, undefined, undefined, [
5843+
DraggableInOnPushDropZone,
5844+
]);
5845+
fixture.detectChanges();
5846+
5847+
const dropZones = Array.from<HTMLElement>(
5848+
fixture.nativeElement.querySelectorAll('.cdk-drop-list'),
5849+
);
5850+
const item = dropZones[0].querySelector('.cdk-drag')!;
5851+
5852+
expect(dropZones.every(c => !c.classList.contains('cdk-drop-list-receiving')))
5853+
.withContext('Expected neither of the containers to have the class.')
5854+
.toBe(true);
5855+
5856+
startDraggingViaMouse(fixture, item);
5857+
fixture.detectChanges();
5858+
5859+
expect(dropZones[0].classList)
5860+
.withContext('Expected source container not to have the receiving class.')
5861+
.not.toContain('cdk-drop-list-receiving');
5862+
5863+
expect(dropZones[1].classList)
5864+
.withContext('Expected target container to have the receiving class.')
5865+
.toContain('cdk-drop-list-receiving');
5866+
}));
5867+
58415868
it(
58425869
'should be able to move the item over an intermediate container before ' +
58435870
'dropping it into the final one',
@@ -6763,11 +6790,22 @@ class DraggableInDropZone implements AfterViewInit {
67636790
}
67646791

67656792
@Component({
6793+
selector: 'draggable-in-on-push-zone',
67666794
template: DROP_ZONE_FIXTURE_TEMPLATE,
67676795
changeDetection: ChangeDetectionStrategy.OnPush,
67686796
})
67696797
class DraggableInOnPushDropZone extends DraggableInDropZone {}
67706798

6799+
@Component({
6800+
template: `
6801+
<div cdkDropListGroup>
6802+
<draggable-in-on-push-zone></draggable-in-on-push-zone>
6803+
<draggable-in-on-push-zone></draggable-in-on-push-zone>
6804+
</div>
6805+
`,
6806+
})
6807+
class ConnectedDropListsInOnPush {}
6808+
67716809
@Component({
67726810
template: DROP_ZONE_FIXTURE_TEMPLATE,
67736811

src/cdk/drag-drop/directives/drop-list.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {DropListRef} from '../drop-list-ref';
3535
import {DragRef} from '../drag-ref';
3636
import {DragDrop} from '../drag-drop';
3737
import {DropListOrientation, DragAxis, DragDropConfig, CDK_DRAG_CONFIG} from './config';
38-
import {Subject} from 'rxjs';
38+
import {merge, Subject} from 'rxjs';
3939
import {startWith, takeUntil} from 'rxjs/operators';
4040
import {assertElementNode} from './assertions';
4141

@@ -375,6 +375,10 @@ export class CdkDropList<T = any> implements OnDestroy {
375375
// detection and we're not guaranteed for something else to have triggered it.
376376
this._changeDetectorRef.markForCheck();
377377
});
378+
379+
merge(ref.receivingStarted, ref.receivingStopped).subscribe(() =>
380+
this._changeDetectorRef.markForCheck(),
381+
);
378382
}
379383

380384
/** Assigns the default input values based on a provided config object. */

src/cdk/drag-drop/drop-list-ref.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ export class DropListRef<T = any> {
130130
item: DragRef;
131131
}>();
132132

133+
/** Emits when a dragging sequence is started in a list connected to the current one. */
134+
readonly receivingStarted = new Subject<{
135+
receiver: DropListRef;
136+
initiator: DropListRef;
137+
items: DragRef[];
138+
}>();
139+
140+
/** Emits when a dragging sequence is stopped from a list connected to the current one. */
141+
readonly receivingStopped = new Subject<{
142+
receiver: DropListRef;
143+
initiator: DropListRef;
144+
}>();
145+
133146
/** Arbitrary data that can be attached to the drop list. */
134147
data: T;
135148

@@ -207,6 +220,8 @@ export class DropListRef<T = any> {
207220
this.exited.complete();
208221
this.dropped.complete();
209222
this.sorted.complete();
223+
this.receivingStarted.complete();
224+
this.receivingStopped.complete();
210225
this._activeSiblings.clear();
211226
this._scrollNode = null!;
212227
this._parentPositions.clear();
@@ -637,6 +652,11 @@ export class DropListRef<T = any> {
637652
activeSiblings.add(sibling);
638653
this._cacheParentPositions();
639654
this._listenToScrollEvents();
655+
this.receivingStarted.next({
656+
initiator: sibling,
657+
receiver: this,
658+
items,
659+
});
640660
}
641661
}
642662

@@ -647,6 +667,7 @@ export class DropListRef<T = any> {
647667
_stopReceiving(sibling: DropListRef) {
648668
this._activeSiblings.delete(sibling);
649669
this._viewportScrollSubscription.unsubscribe();
670+
this.receivingStopped.next({initiator: sibling, receiver: this});
650671
}
651672

652673
/**

tools/public_api_guard/cdk/drag-drop.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,15 @@ export class DropListRef<T = any> {
494494
_isOverContainer(x: number, y: number): boolean;
495495
isReceiving(): boolean;
496496
lockAxis: 'x' | 'y';
497+
readonly receivingStarted: Subject<{
498+
receiver: DropListRef;
499+
initiator: DropListRef;
500+
items: DragRefInternal[];
501+
}>;
502+
readonly receivingStopped: Subject<{
503+
receiver: DropListRef;
504+
initiator: DropListRef;
505+
}>;
497506
readonly sorted: Subject<{
498507
previousIndex: number;
499508
currentIndex: number;

0 commit comments

Comments
 (0)