diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts
index b3248a464f06..9a085349d50f 100644
--- a/src/cdk/drag-drop/directives/drag.spec.ts
+++ b/src/cdk/drag-drop/directives/drag.spec.ts
@@ -475,6 +475,27 @@ describe('CdkDrag', () => {
it('should be able to set an alternate drag root element', fakeAsync(() => {
const fixture = createComponent(DraggableWithAlternateRoot);
+ fixture.componentInstance.rootElementSelector = '.alternate-root';
+ fixture.detectChanges();
+
+ const dragRoot = fixture.componentInstance.dragRoot.nativeElement;
+ const dragElement = fixture.componentInstance.dragElement.nativeElement;
+
+ expect(dragRoot.style.transform).toBeFalsy();
+ expect(dragElement.style.transform).toBeFalsy();
+
+ dragElementViaMouse(fixture, dragRoot, 50, 100);
+
+ expect(dragRoot.style.transform).toBe('translate3d(50px, 100px, 0px)');
+ expect(dragElement.style.transform).toBeFalsy();
+ }));
+
+ it('should handle the root element selector changing after init', fakeAsync(() => {
+ const fixture = createComponent(DraggableWithAlternateRoot);
+ fixture.detectChanges();
+ tick();
+
+ fixture.componentInstance.rootElementSelector = '.alternate-root';
fixture.detectChanges();
const dragRoot = fixture.componentInstance.dragRoot.nativeElement;
@@ -2999,7 +3020,7 @@ class ConnectedDropZonesViaGroupDirective extends ConnectedDropZones {
@@ -3009,6 +3030,7 @@ class DraggableWithAlternateRoot {
@ViewChild('dragElement') dragElement: ElementRef;
@ViewChild('dragRoot') dragRoot: ElementRef;
@ViewChild(CdkDrag) dragInstance: CdkDrag;
+ rootElementSelector: string;
}
diff --git a/src/cdk/drag-drop/directives/drag.ts b/src/cdk/drag-drop/directives/drag.ts
index b857318c78aa..bfbaf292e065 100644
--- a/src/cdk/drag-drop/directives/drag.ts
+++ b/src/cdk/drag-drop/directives/drag.ts
@@ -26,6 +26,8 @@ import {
QueryList,
SkipSelf,
ViewContainerRef,
+ OnChanges,
+ SimpleChanges,
} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Observable, Subscription, Observer} from 'rxjs';
@@ -70,7 +72,7 @@ export function CDK_DRAG_CONFIG_FACTORY(): DragRefConfig {
},
providers: [{provide: CDK_DRAG_PARENT, useExisting: CdkDrag}]
})
-export class CdkDrag implements AfterViewInit, OnDestroy {
+export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy {
/** Subscription to the stream that initializes the root element. */
private _rootElementInitSubscription = Subscription.EMPTY;
@@ -214,14 +216,7 @@ export class CdkDrag implements AfterViewInit, OnDestroy {
this._rootElementInitSubscription = this._ngZone.onStable.asObservable()
.pipe(take(1))
.subscribe(() => {
- const rootElement = this._getRootElement();
-
- if (rootElement.nodeType !== this._document.ELEMENT_NODE) {
- throw Error(`cdkDrag must be attached to an element node. ` +
- `Currently attached to "${rootElement.nodeName}".`);
- }
-
- this._dragRef.withRootElement(rootElement);
+ this._updateRootElement();
this._handles.changes
.pipe(startWith(this._handles))
.subscribe((handleList: QueryList) => {
@@ -230,18 +225,33 @@ export class CdkDrag implements AfterViewInit, OnDestroy {
});
}
+ ngOnChanges(changes: SimpleChanges) {
+ const rootSelectorChange = changes.rootElementSelector;
+
+ // We don't have to react to the first change since it's being
+ // handled in `ngAfterViewInit` where it needs to be deferred.
+ if (rootSelectorChange && !rootSelectorChange.firstChange) {
+ this._updateRootElement();
+ }
+ }
+
ngOnDestroy() {
this._rootElementInitSubscription.unsubscribe();
this._dragRef.dispose();
}
- /** Gets the root draggable element, based on the `rootElementSelector`. */
- private _getRootElement(): HTMLElement {
+ /** Syncs the root element with the `DragRef`. */
+ private _updateRootElement() {
const element = this.element.nativeElement;
const rootElement = this.rootElementSelector ?
- getClosestMatchingAncestor(element, this.rootElementSelector) : null;
+ getClosestMatchingAncestor(element, this.rootElementSelector) : element;
+
+ if (rootElement && rootElement.nodeType !== this._document.ELEMENT_NODE) {
+ throw Error(`cdkDrag must be attached to an element node. ` +
+ `Currently attached to "${rootElement.nodeName}".`);
+ }
- return rootElement || element;
+ this._dragRef.withRootElement(rootElement || element);
}
/** Gets the boundary element, based on the `boundaryElementSelector`. */
diff --git a/tools/public_api_guard/cdk/drag-drop.d.ts b/tools/public_api_guard/cdk/drag-drop.d.ts
index 6dc3498ec12f..53ab39df9819 100644
--- a/tools/public_api_guard/cdk/drag-drop.d.ts
+++ b/tools/public_api_guard/cdk/drag-drop.d.ts
@@ -6,7 +6,7 @@ export declare const CDK_DROP_LIST: InjectionToken>;
export declare const CDK_DROP_LIST_CONTAINER: InjectionToken>;
-export declare class CdkDrag implements AfterViewInit, OnDestroy {
+export declare class CdkDrag implements AfterViewInit, OnChanges, OnDestroy {
_dragRef: DragRef>;
_handles: QueryList;
_placeholderTemplate: CdkDragPlaceholder;
@@ -31,6 +31,7 @@ export declare class CdkDrag implements AfterViewInit, OnDestroy {
getPlaceholderElement(): HTMLElement;
getRootElement(): HTMLElement;
ngAfterViewInit(): void;
+ ngOnChanges(changes: SimpleChanges): void;
ngOnDestroy(): void;
reset(): void;
}