Skip to content

Commit 12c15ba

Browse files
crisbetojelbourn
authored andcommitted
fix(drag-drop): drop list not toggling dragging class inside component with OnPush change detection (#13703)
Fixes the `CdkDropList` not toggling its `cdk-drop-list-dragging` class if it's placed inside a component with `OnPush` change detection. Fixes #13680.
1 parent d0d97c3 commit 12c15ba

File tree

3 files changed

+50
-20
lines changed

3 files changed

+50
-20
lines changed

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

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ViewChild,
1010
ViewChildren,
1111
ViewEncapsulation,
12+
ChangeDetectionStrategy,
1213
} from '@angular/core';
1314
import {TestBed, ComponentFixture, fakeAsync, flush, tick} from '@angular/core/testing';
1415
import {DragDropModule} from './drag-drop-module';
@@ -547,6 +548,27 @@ describe('CdkDrag', () => {
547548
expect(dropZone.element.nativeElement.classList).not.toContain('cdk-drop-dragging');
548549
}));
549550

551+
it('should toggle a class when the user starts dragging an item with OnPush change detection',
552+
fakeAsync(() => {
553+
const fixture = createComponent(DraggableInOnPushDropZone);
554+
fixture.detectChanges();
555+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
556+
const dropZone = fixture.componentInstance.dropInstance;
557+
558+
expect(dropZone.element.nativeElement.classList).not.toContain('cdk-drop-list-dragging');
559+
560+
startDraggingViaMouse(fixture, item);
561+
562+
expect(dropZone.element.nativeElement.classList).toContain('cdk-drop-list-dragging');
563+
564+
dispatchMouseEvent(document, 'mouseup');
565+
fixture.detectChanges();
566+
flush();
567+
fixture.detectChanges();
568+
569+
expect(dropZone.element.nativeElement.classList).not.toContain('cdk-drop-dragging');
570+
}));
571+
550572
it('should not toggle dragging class if the element was not dragged more than the threshold',
551573
fakeAsync(() => {
552574
const fixture = createComponent(DraggableInDropZone, [], 5);
@@ -1991,24 +2013,24 @@ class StandaloneDraggableWithMultipleHandles {
19912013
@ViewChildren(CdkDragHandle) handles: QueryList<CdkDragHandle>;
19922014
}
19932015

1994-
@Component({
1995-
template: `
2016+
const DROP_ZONE_FIXTURE_TEMPLATE = `
2017+
<div
2018+
cdkDropList
2019+
style="width: 100px; background: pink;"
2020+
[id]="dropZoneId"
2021+
[cdkDropListData]="items"
2022+
(cdkDropListDropped)="droppedSpy($event)">
19962023
<div
1997-
cdkDropList
1998-
style="width: 100px; background: pink;"
1999-
[id]="dropZoneId"
2000-
[cdkDropListData]="items"
2001-
(cdkDropListDropped)="droppedSpy($event)">
2002-
<div
2003-
*ngFor="let item of items"
2004-
cdkDrag
2005-
[cdkDragData]="item"
2006-
[style.height.px]="item.height"
2007-
[style.margin-bottom.px]="item.margin"
2008-
style="width: 100%; background: red;">{{item.value}}</div>
2009-
</div>
2010-
`
2011-
})
2024+
*ngFor="let item of items"
2025+
cdkDrag
2026+
[cdkDragData]="item"
2027+
[style.height.px]="item.height"
2028+
[style.margin-bottom.px]="item.margin"
2029+
style="width: 100%; background: red;">{{item.value}}</div>
2030+
</div>
2031+
`;
2032+
2033+
@Component({template: DROP_ZONE_FIXTURE_TEMPLATE})
20122034
class DraggableInDropZone {
20132035
@ViewChildren(CdkDrag) dragItems: QueryList<CdkDrag>;
20142036
@ViewChild(CdkDropList) dropInstance: CdkDropList;
@@ -2024,6 +2046,12 @@ class DraggableInDropZone {
20242046
});
20252047
}
20262048

2049+
@Component({
2050+
template: DROP_ZONE_FIXTURE_TEMPLATE,
2051+
changeDetection: ChangeDetectionStrategy.OnPush,
2052+
})
2053+
class DraggableInOnPushDropZone extends DraggableInDropZone {}
2054+
20272055

20282056
@Component({
20292057
encapsulation: ViewEncapsulation.None,

src/cdk/drag-drop/drag.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
SkipSelf,
2929
ViewContainerRef,
3030
} from '@angular/core';
31-
import {supportsPassiveEventListeners} from '@angular/cdk/platform';
31+
import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
3232
import {Observable, Subject, Subscription, Observer} from 'rxjs';
3333
import {take} from 'rxjs/operators';
3434
import {DragDropRegistry} from './drag-drop-registry';
@@ -80,8 +80,7 @@ export function CDK_DRAG_CONFIG_FACTORY(): CdkDragConfig {
8080
}
8181

8282
/** Options that can be used to bind a passive event listener. */
83-
const passiveEventListenerOptions = supportsPassiveEventListeners() ?
84-
{passive: true} as EventListenerOptions : false;
83+
const passiveEventListenerOptions = normalizePassiveListenerOptions({passive: true});
8584

8685
/** Element that can be moved inside a CdkDropList container. */
8786
@Directive({

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
QueryList,
2020
Optional,
2121
Directive,
22+
ChangeDetectorRef,
2223
} from '@angular/core';
2324
import {Directionality} from '@angular/cdk/bidi';
2425
import {CdkDrag} from './drag';
@@ -141,6 +142,7 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
141142
constructor(
142143
public element: ElementRef<HTMLElement>,
143144
private _dragDropRegistry: DragDropRegistry<CdkDrag, CdkDropList<T>>,
145+
private _changeDetectorRef: ChangeDetectorRef,
144146
@Optional() private _dir?: Directionality) {}
145147

146148
ngOnInit() {
@@ -175,6 +177,7 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
175177
this._dragging = true;
176178
this._activeDraggables = this._draggables.toArray();
177179
this._cachePositions();
180+
this._changeDetectorRef.markForCheck();
178181
}
179182

180183
/**

0 commit comments

Comments
 (0)