diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index 0f7504b58303..ae401cc1d859 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -461,6 +461,25 @@ describe('CdkDrag', () => { expect(element.classList).not.toContain('cdk-drag-dragging'); })); + it('should add a class while an element is being dragged with OnPush change detection', + fakeAsync(() => { + const fixture = createComponent(StandaloneDraggableWithOnPush); + fixture.detectChanges(); + + const element = fixture.componentInstance.dragElement.nativeElement; + + expect(element.classList).not.toContain('cdk-drag-dragging'); + + startDraggingViaMouse(fixture, element); + + expect(element.classList).toContain('cdk-drag-dragging'); + + dispatchMouseEvent(document, 'mouseup'); + fixture.detectChanges(); + + expect(element.classList).not.toContain('cdk-drag-dragging'); + })); + it('should not add a class if item was not dragged more than the threshold', fakeAsync(() => { const fixture = createComponent(StandaloneDraggable, [], 5); fixture.detectChanges(); @@ -2821,6 +2840,17 @@ class StandaloneDraggable { boundarySelector: string; } +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+ ` +}) +class StandaloneDraggableWithOnPush { + @ViewChild('dragElement') dragElement: ElementRef; + @ViewChild(CdkDrag) dragInstance: CdkDrag; +} + @Component({ template: ` implements AfterViewInit, OnChanges, OnDestroy { @Optional() private _dir: Directionality, /** - * @deprecated `viewportRuler` and `dragDropRegistry` parameters + * @deprecated `viewportRuler`, `dragDropRegistry` and `_changeDetectorRef` parameters * to be removed. Also `dragDrop` parameter to be made required. * @breaking-change 8.0.0. */ - dragDrop?: DragDrop) { + dragDrop?: DragDrop, + private _changeDetectorRef?: ChangeDetectorRef) { // @breaking-change 8.0.0 Remove null check once the paramter is made required. @@ -191,7 +193,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { this._dragRef.data = this; this._syncInputs(this._dragRef); - this._proxyEvents(this._dragRef); + this._handleEvents(this._dragRef); } /** @@ -312,13 +314,17 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { }); } - /** - * Proxies the events from a DragRef to events that - * match the interfaces of the CdkDrag outputs. - */ - private _proxyEvents(ref: DragRef>) { + /** Handles the events from the underlying `DragRef`. */ + private _handleEvents(ref: DragRef>) { ref.started.subscribe(() => { this.started.emit({source: this}); + + // Since all of these events run outside of change detection, + // we need to ensure that everything is marked correctly. + if (this._changeDetectorRef) { + // @breaking-change 8.0.0 Remove null check for _changeDetectorRef + this._changeDetectorRef.markForCheck(); + } }); ref.released.subscribe(() => { @@ -327,6 +333,13 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { ref.ended.subscribe(() => { this.ended.emit({source: this}); + + // Since all of these events run outside of change detection, + // we need to ensure that everything is marked correctly. + if (this._changeDetectorRef) { + // @breaking-change 8.0.0 Remove null check for _changeDetectorRef + this._changeDetectorRef.markForCheck(); + } }); ref.entered.subscribe(event => { diff --git a/tools/public_api_guard/cdk/drag-drop.d.ts b/tools/public_api_guard/cdk/drag-drop.d.ts index 1ded5a5d2007..e314d47d2172 100644 --- a/tools/public_api_guard/cdk/drag-drop.d.ts +++ b/tools/public_api_guard/cdk/drag-drop.d.ts @@ -28,7 +28,7 @@ export declare class CdkDrag implements AfterViewInit, OnChanges, OnDes constructor( element: ElementRef, dropContainer: CdkDropList, _document: any, _ngZone: NgZone, _viewContainerRef: ViewContainerRef, viewportRuler: ViewportRuler, dragDropRegistry: DragDropRegistry, config: DragRefConfig, _dir: Directionality, - dragDrop?: DragDrop); + dragDrop?: DragDrop, _changeDetectorRef?: ChangeDetectorRef | undefined); getPlaceholderElement(): HTMLElement; getRootElement(): HTMLElement; ngAfterViewInit(): void;