@@ -20,6 +20,8 @@ import {
2020 EventEmitter ,
2121 ViewContainerRef ,
2222 EmbeddedViewRef ,
23+ ContentChildren ,
24+ QueryList ,
2325} from '@angular/core' ;
2426import { CdkDragHandle } from './drag-handle' ;
2527import { DOCUMENT } from '@angular/platform-browser' ;
@@ -42,6 +44,8 @@ const activeEventOptions = supportsPassiveEventListeners() ? {passive: false} :
4244 exportAs : 'cdkDrag' ,
4345 host : {
4446 'class' : 'cdk-drag' ,
47+ '(mousedown)' : '_startDragging($event)' ,
48+ '(touchstart)' : '_startDragging($event)' ,
4549 }
4650} )
4751export class CdkDrag implements AfterContentInit , OnDestroy {
@@ -88,8 +92,8 @@ export class CdkDrag implements AfterContentInit, OnDestroy {
8892 /** Cached scroll position on the page when the element was picked up. */
8993 private _scrollPosition : { top : number , left : number } ;
9094
91- /** Element that can be used to drag the draggable item. */
92- @ContentChild ( CdkDragHandle ) _handle : CdkDragHandle ;
95+ /** Elements that can be used to drag the draggable item. */
96+ @ContentChildren ( CdkDragHandle ) _handles : QueryList < CdkDragHandle > ;
9397
9498 /** Element that will be used as a template to create the draggable item's preview. */
9599 @ContentChild ( CdkDragPreview ) _previewTemplate : CdkDragPreview ;
@@ -135,11 +139,6 @@ export class CdkDrag implements AfterContentInit, OnDestroy {
135139 }
136140
137141 ngAfterContentInit ( ) {
138- // TODO: doesn't handle (pun intended) the handle being destroyed
139- const dragElement = ( this . _handle ? this . _handle . element : this . element ) . nativeElement ;
140- dragElement . addEventListener ( 'mousedown' , this . _pointerDown ) ;
141- dragElement . addEventListener ( 'touchstart' , this . _pointerDown ) ;
142-
143142 // WebKit won't preventDefault on a dynamically-added `touchmove` listener, which means that
144143 // we need to add one ahead of time. See https://bugs.webkit.org/show_bug.cgi?id=184250.
145144 // TODO: move into a central registry.
@@ -162,8 +161,27 @@ export class CdkDrag implements AfterContentInit, OnDestroy {
162161 }
163162 }
164163
164+ /** Starts the dragging sequence. */
165+ _startDragging ( event : MouseEvent | TouchEvent ) {
166+ // Delegate the event based on whether it started from a handle or the element itself.
167+ if ( this . _handles . length ) {
168+ const targetHandle = this . _handles . find ( handle => {
169+ const element = handle . element . nativeElement ;
170+ const target = event . target ;
171+ return ! ! target && ( target === element || element . contains ( target as HTMLElement ) ) ;
172+ } ) ;
173+
174+ if ( targetHandle ) {
175+ this . _pointerDown ( targetHandle . element , event ) ;
176+ }
177+ } else {
178+ this . _pointerDown ( this . element , event ) ;
179+ }
180+ }
181+
165182 /** Handler for when the pointer is pressed down on the element or the handle. */
166- private _pointerDown = ( event : MouseEvent | TouchEvent ) => {
183+ private _pointerDown = ( referenceElement : ElementRef < HTMLElement > ,
184+ event : MouseEvent | TouchEvent ) => {
167185 if ( this . _isDragging ) {
168186 return ;
169187 }
@@ -175,7 +193,7 @@ export class CdkDrag implements AfterContentInit, OnDestroy {
175193 // If we have a custom preview template, the element won't be visible anyway so we avoid the
176194 // extra `getBoundingClientRect` calls and just move the preview next to the cursor.
177195 this . _pickupPositionInElement = this . _previewTemplate ? { x : 0 , y : 0 } :
178- this . _getPointerPositionInElement ( event ) ;
196+ this . _getPointerPositionInElement ( referenceElement , event ) ;
179197 this . _pickupPositionOnPage = this . _getPointerPositionOnPage ( event ) ;
180198 this . _registerMoveListeners ( event ) ;
181199
@@ -371,11 +389,13 @@ export class CdkDrag implements AfterContentInit, OnDestroy {
371389
372390 /**
373391 * Figures out the coordinates at which an element was picked up.
392+ * @param referenceElement Element that initiated the dragging.
374393 * @param event Event that initiated the dragging.
375394 */
376- private _getPointerPositionInElement ( event : MouseEvent | TouchEvent ) : Point {
395+ private _getPointerPositionInElement ( referenceElement : ElementRef < HTMLElement > ,
396+ event : MouseEvent | TouchEvent ) : Point {
377397 const elementRect = this . element . nativeElement . getBoundingClientRect ( ) ;
378- const handleElement = this . _handle ? this . _handle . element . nativeElement : null ;
398+ const handleElement = referenceElement === this . element ? null : referenceElement . nativeElement ;
379399 const referenceRect = handleElement ? handleElement . getBoundingClientRect ( ) : elementRect ;
380400 const x = this . _isTouchEvent ( event ) ?
381401 event . targetTouches [ 0 ] . pageX - referenceRect . left - this . _scrollPosition . left :
0 commit comments