Skip to content

Instantly share code, notes, and snippets.

@shlomiassaf
Created November 15, 2018 23:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shlomiassaf/49126eed76925a90ccfe6bb6c78bfddc to your computer and use it in GitHub Desktop.
Save shlomiassaf/49126eed76925a90ccfe6bb6c78bfddc to your computer and use it in GitHub Desktop.
Lazy binding between `CdkDrag` and `CdkDropList` + support for non direct draggables in a drop container
import { take } from 'rxjs/operators';
import { Input, Directive, ElementRef, QueryList, OnDestroy, Optional, AfterViewInit } from '@angular/core';
import { CdkDropList, CdkDrag, CdkDragHandle, CDK_DROP_LIST_CONTAINER } from '@angular/cdk/drag-drop';
@Directive({
selector: '[cdkLazyDropList]',
exportAs: 'cdkLazyDropList',
providers: [
{ provide: CDK_DROP_LIST_CONTAINER, useExisting: CdkLazyDropList },
],
host: { // tslint:disable-line:use-host-property-decorator
'class': 'cdk-drop-list',
'[id]': 'id',
'[class.cdk-drop-list-dragging]': '_dragging'
}
})
export class CdkLazyDropList<T = any> extends CdkDropList<T> {
/**
* Selector that will be used to determine the direct container element, starting from
* the `cdkDropList` element and going down the DOM. Passing an alternate direct container element
* is useful when the `cdkDropList` is not the direct parent (i.e. ancestor but not father)
* of the draggable elements.
*/
// tslint:disable-next-line:no-input-rename
@Input('cdkDropListDirectContainerElement') directContainerElement: string;
_draggables: QueryList<CdkDrag>;
private _draggablesSet = new Set<CdkDrag>();
// This is a workaround for https://github.com/angular/material2/pull/14153
// Working around the missing capability for selecting a container element that is not the drop container host.
// The entire enter() overriding method can be removed if PR accepted, along with `directContainerElement`
enter(item: CdkDrag, pointerX: number, pointerY: number): void {
super.enter(item, pointerX, pointerY);
if (this.directContainerElement) {
const placeholder = item.getPlaceholderElement();
if (this.element.nativeElement.lastChild === placeholder) {
const element = this.element.nativeElement.querySelector(this.directContainerElement);
if (element) {
element.appendChild(item.getPlaceholderElement());
}
}
}
}
addDrag(drag: CdkDrag): void {
this._draggablesSet.add(drag);
this._draggables.reset(Array.from(this._draggablesSet.values()));
}
removeDrag(drag: CdkDrag): boolean {
const result = this._draggablesSet.delete(drag);
if (result) {
this._draggables.reset(Array.from(this._draggablesSet.values()));
}
return result;
}
}
@Directive({
selector: '[cdkLazyDrag]',
exportAs: 'cdkLazyDrag',
host: { // tslint:disable-line:use-host-property-decorator
'class': 'cdk-drag',
'[class.cdk-drag-dragging]': '_hasStartedDragging && _isDragging()',
},
})
export class CdkLazyDrag<T = any, Z extends CdkLazyDropList<T> = CdkLazyDropList<T>> extends CdkDrag<T> implements AfterViewInit, OnDestroy {
@Input() get cdkDropList(): Z { return this.dropContainer as Z; }
set cdkDropList(value: Z) {
// TO SUPPORT `cdkDropList` via string input (ID) we need a reactive registry...
if (this.cdkDropList) {
this.cdkDropList.removeDrag(this);
}
this.dropContainer = value;
if (value) {
value.addDrag(this);
}
}
// This is a workaround for https://github.com/angular/material2/pull/14158
// Working around the issue of drop container is not the direct parent (father) of a drag item.
// The entire ngAfterViewInit() overriding method can be removed if PR accepted.
ngAfterViewInit(): void {
this.started.subscribe( startedEvent => {
if (this.dropContainer) {
const element = this.getRootElement();
const initialRootElementParent = element.parentNode as HTMLElement;
if (!element.nextSibling && initialRootElementParent !== this.dropContainer.element.nativeElement) {
this.ended.pipe(take(1)).subscribe( endedEvent => initialRootElementParent.appendChild(element) );
}
}
});
super.ngAfterViewInit();
}
ngOnDestroy(): void {
if (this.cdkDropList) {
this.cdkDropList.removeDrag(this);
}
super.ngOnDestroy();
}
}
@negue
Copy link

negue commented Jun 21, 2019

Is there any chance to get this to work with Angular 8 too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment