diff --git a/src/cdk/drag-drop/drag-drop-module.ts b/src/cdk/drag-drop/drag-drop-module.ts
index c0f95b36014..b6237a9bb8c 100644
--- a/src/cdk/drag-drop/drag-drop-module.ts
+++ b/src/cdk/drag-drop/drag-drop-module.ts
@@ -8,6 +8,7 @@
import {NgModule} from '@angular/core';
import {CdkDropList} from './drop-list';
+import {CdkDropListGroup} from './drop-list-group';
import {CdkDrag} from './drag';
import {CdkDragHandle} from './drag-handle';
import {CdkDragPreview} from './drag-preview';
@@ -16,6 +17,7 @@ import {CdkDragPlaceholder} from './drag-placeholder';
@NgModule({
declarations: [
CdkDropList,
+ CdkDropListGroup,
CdkDrag,
CdkDragHandle,
CdkDragPreview,
@@ -23,6 +25,7 @@ import {CdkDragPlaceholder} from './drag-placeholder';
],
exports: [
CdkDropList,
+ CdkDropListGroup,
CdkDrag,
CdkDragHandle,
CdkDragPreview,
diff --git a/src/cdk/drag-drop/drag-drop.md b/src/cdk/drag-drop/drag-drop.md
index 3a5764949fc..81144c75feb 100644
--- a/src/cdk/drag-drop/drag-drop.md
+++ b/src/cdk/drag-drop/drag-drop.md
@@ -22,12 +22,12 @@ update the data model once the user finishes dragging.
### Transferring items between lists
The `cdkDropList` directive supports transferring dragged items between connected drop zones.
You can connect one or more `cdkDropList` instances together by setting the `cdkDropListConnectedTo`
-property.
+property or by wrapping the elements in an element with the `cdkDropListGroup` attribute.
-Note that `cdkDropListConnectedTo` works both with a direct reference to another `cdkDropList`, or by
-referencing the `id` of another drop container:
+Note that `cdkDropListConnectedTo` works both with a direct reference to another `cdkDropList`, or
+by referencing the `id` of another drop container:
```html
@@ -39,6 +39,19 @@ referencing the `id` of another drop container:
```
+If you have an unknown number of connected drop lists, you can use the `cdkDropListGroup` directive
+to set up the connection automatically. Note that any new `cdkDropList` that is added under a group
+will be connected to all other automatically.
+
+```html
+
+```
+
+
+
### Attaching data
You can associate some arbitrary data with both `cdkDrag` and `cdkDropList` by setting `cdkDragData`
or `cdkDropListData`, respectively. Events fired from both directives include this data, allowing
diff --git a/src/cdk/drag-drop/drag.spec.ts b/src/cdk/drag-drop/drag.spec.ts
index a5b7b781b7b..971b4d7b31b 100644
--- a/src/cdk/drag-drop/drag.spec.ts
+++ b/src/cdk/drag-drop/drag.spec.ts
@@ -1927,6 +1927,31 @@ describe('CdkDrag', () => {
});
}));
+ it('should be able to connect two drop zones using the drop list group', fakeAsync(() => {
+ const fixture = createComponent(ConnectedDropZonesViaGroupDirective);
+ fixture.detectChanges();
+
+ const dropInstances = fixture.componentInstance.dropInstances.toArray();
+ const groups = fixture.componentInstance.groupedDragItems;
+ const element = groups[0][1].element.nativeElement;
+ const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
+
+ dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1);
+ flush();
+ fixture.detectChanges();
+
+ const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
+
+ expect(event).toBeTruthy();
+ expect(event).toEqual({
+ previousIndex: 1,
+ currentIndex: 3,
+ item: groups[0][1],
+ container: dropInstances[1],
+ previousContainer: dropInstances[0]
+ });
+ }));
+
it('should be able to pass a single id to `connectedTo`', fakeAsync(() => {
const fixture = createComponent(ConnectedDropZones);
fixture.detectChanges();
@@ -2311,6 +2336,42 @@ class ConnectedDropZones implements AfterViewInit {
}
}
+@Component({
+ encapsulation: ViewEncapsulation.None,
+ styles: [`
+ .cdk-drop-list {
+ display: block;
+ width: 100px;
+ min-height: ${ITEM_HEIGHT}px;
+ background: hotpink;
+ }
+
+ .cdk-drag {
+ display: block;
+ height: ${ITEM_HEIGHT}px;
+ background: red;
+ }
+ `],
+ template: `
+
+ `
+})
+class ConnectedDropZonesViaGroupDirective extends ConnectedDropZones {}
+
@Component({
template: `
diff --git a/src/cdk/drag-drop/drop-list-group.ts b/src/cdk/drag-drop/drop-list-group.ts
new file mode 100644
index 00000000000..b6a97bbf265
--- /dev/null
+++ b/src/cdk/drag-drop/drop-list-group.ts
@@ -0,0 +1,27 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Directive, OnDestroy} from '@angular/core';
+
+/**
+ * Declaratively connects sibling `cdkDropList` instances together. All of the `cdkDropList`
+ * elements that are placed inside a `cdkDropListGroup` will be connected to each other
+ * automatically. Can be used as an alternative to the `cdkDropListConnectedTo` input
+ * from `cdkDropList`.
+ */
+@Directive({
+ selector: '[cdkDropListGroup]'
+})
+export class CdkDropListGroup implements OnDestroy {
+ /** Drop lists registered inside the group. */
+ readonly _items = new Set();
+
+ ngOnDestroy() {
+ this._items.clear();
+ }
+}
diff --git a/src/cdk/drag-drop/drop-list.ts b/src/cdk/drag-drop/drop-list.ts
index 7e4add8e0f3..25a7437a2ce 100644
--- a/src/cdk/drag-drop/drop-list.ts
+++ b/src/cdk/drag-drop/drop-list.ts
@@ -27,6 +27,7 @@ import {DragDropRegistry} from './drag-drop-registry';
import {CdkDragDrop, CdkDragEnter, CdkDragExit} from './drag-events';
import {moveItemInArray} from './drag-utils';
import {CDK_DROP_LIST_CONTAINER} from './drop-list-container';
+import {CdkDropListGroup} from './drop-list-group';
/** Counter used to generate unique ids for drop zones. */
@@ -143,14 +144,23 @@ export class CdkDropList implements OnInit, OnDestroy {
public element: ElementRef,
private _dragDropRegistry: DragDropRegistry>,
private _changeDetectorRef: ChangeDetectorRef,
- @Optional() private _dir?: Directionality) {}
+ @Optional() private _dir?: Directionality,
+ @Optional() private _group?: CdkDropListGroup) {}
ngOnInit() {
this._dragDropRegistry.registerDropContainer(this);
+
+ if (this._group) {
+ this._group._items.add(this);
+ }
}
ngOnDestroy() {
this._dragDropRegistry.removeDropContainer(this);
+
+ if (this._group) {
+ this._group._items.delete(this);
+ }
}
/** Whether an item in the container is being dragged. */
@@ -366,6 +376,8 @@ export class CdkDropList implements OnInit, OnDestroy {
/** Refreshes the position cache of the items and sibling containers. */
private _cachePositions() {
const isHorizontal = this.orientation === 'horizontal';
+
+ this._positionCache.self = this.element.nativeElement.getBoundingClientRect();
this._positionCache.items = this._activeDraggables
.map(drag => {
const elementToMeasure = this._dragDropRegistry.isDragging(drag) ?
@@ -397,12 +409,10 @@ export class CdkDropList implements OnInit, OnDestroy {
a.clientRect.top - b.clientRect.top;
});
- this._positionCache.siblings = coerceArray(this.connectedTo)
- .map(drop => typeof drop === 'string' ? this._dragDropRegistry.getDropContainer(drop)! : drop)
- .filter(drop => drop && drop !== this)
- .map(drop => ({drop, clientRect: drop.element.nativeElement.getBoundingClientRect()}));
-
- this._positionCache.self = this.element.nativeElement.getBoundingClientRect();
+ this._positionCache.siblings = this._getConnectedLists().map(drop => ({
+ drop,
+ clientRect: drop.element.nativeElement.getBoundingClientRect()
+ }));
}
/** Resets the container to its initial state. */
@@ -535,6 +545,23 @@ export class CdkDropList implements OnInit, OnDestroy {
return siblingOffset;
}
+
+ /** Gets an array of unique drop lists that the current list is connected to. */
+ private _getConnectedLists(): CdkDropList[] {
+ const siblings = coerceArray(this.connectedTo).map(drop => {
+ return typeof drop === 'string' ? this._dragDropRegistry.getDropContainer(drop)! : drop;
+ });
+
+ if (this._group) {
+ this._group._items.forEach(drop => {
+ if (siblings.indexOf(drop) === -1) {
+ siblings.push(drop);
+ }
+ });
+ }
+
+ return siblings.filter(drop => drop && drop !== this);
+ }
}
diff --git a/src/cdk/drag-drop/public-api.ts b/src/cdk/drag-drop/public-api.ts
index 15623f5425a..a4f5b173002 100644
--- a/src/cdk/drag-drop/public-api.ts
+++ b/src/cdk/drag-drop/public-api.ts
@@ -7,6 +7,7 @@
*/
export * from './drop-list';
+export * from './drop-list-group';
export * from './drop-list-container';
export * from './drag';
export * from './drag-handle';
diff --git a/src/demo-app/drag-drop/drag-drop-demo.html b/src/demo-app/drag-drop/drag-drop-demo.html
index 810a8061475..342562ed031 100644
--- a/src/demo-app/drag-drop/drag-drop-demo.html
+++ b/src/demo-app/drag-drop/drag-drop-demo.html
@@ -1,4 +1,4 @@
-
+
To do
@@ -7,11 +7,9 @@
+ [cdkDropListData]="todo">
{{item}}
@@ -23,11 +21,9 @@
Done
+ [cdkDropListData]="done">
{{item}}