Angular 嵌套拖放 / CDK Material cdkDropListGroup cdkDropList 嵌套
Angular Nested Drag and Drop / CDK Material cdkDropListGroup cdkDropList nested
我使用 CDK Material 拖放实用程序来创建一个支持拖放的表单编辑器。
它工作正常,但在 cdkDropListGroup
中嵌套 cdkDropList
不起作用。
我无法将任何内容拖到嵌套的下拉列表容器中。
<div class="container">
<div class="row" cdkDropListGroup>
<div class="col-2">
<div id="toolbox" cdkDropList>
...
</div>
</div>
<div class="col-10">
<div id="formContainer" cdkDropList>
...
<div class="row">
<div class="col-md-6" cdkDropList>
... column 1 content
</div>
<div class="col-md-6" cdkDropList>
... column 1 content
</div>
</div>
</div>
</div>
</div>
</div>
我花了一些时间,但由于这些帖子的提示,我终于找到了解决方案:
问题是 cdkDropListGroup
不支持嵌套的下拉列表。您需要使用 [cdkDropListConnectedTo]
绑定连接下拉列表。
但是如果您只将列表连接到数组以进行 [cdkDropListConnectedTo]
绑定,则列表顺序会影响放置行为。此外,在嵌套下拉列表中排序将不起作用。
为避免这些问题,您需要创建一个在拖动时寻找正确 cdkDropList
的服务。
export class DragDropService {
dropLists: CdkDropList[] = [];
currentHoverDropListId?: string;
constructor(@Inject(DOCUMENT) private document: Document) {}
public register(dropList: CdkDropList) {
this.dropLists.push(dropList);
}
dragMoved(event: CdkDragMove<IFormControl>) {
let elementFromPoint = this.document.elementFromPoint(
event.pointerPosition.x,
event.pointerPosition.y
);
if (!elementFromPoint) {
this.currentHoverDropListId = undefined;
return;
}
let dropList = elementFromPoint.classList.contains('cdk-drop-list')
? elementFromPoint
: elementFromPoint.closest('.cdk-drop-list');
if (!dropList) {
this.currentHoverDropListId = undefined;
return;
}
this.currentHoverDropListId = dropList.id;
}
dragReleased(event: CdkDragRelease) {
this.currentHoverDropListId = undefined;
}
}
register
向每个 cdkDropList
.
使用的 dropList
数组添加一个新的下拉列表
dragMoved
确定鼠标指针下方的正确 cdkDropList
。
最好的办法是创建一个包含 cdkDropList
.
的自己的组件
以下组件仅用于简单和演示目的。您不应直接使用服务属性。
<div
*ngIf="container"
cdkDropList
[cdkDropListData]="container.controls"
[cdkDropListConnectedTo]="dragDropService.dropLists"
[cdkDropListEnterPredicate]="allowDropPredicate"
(cdkDropListDropped)="dropped($event)"
>
<div
*ngFor="let item of container.controls"
cdkDrag
[cdkDragData]="item"
(cdkDragMoved)="dragMoved($event)"
(cdkDragReleased)="dragReleased($event)"
>
Drag Content
</div>
</div>
export class FormContainerComponent implements OnInit, AfterViewInit {
@ViewChild(CdkDropList) dropList?: CdkDropList;
@Input() container: IFormContainer | undefined;
allowDropPredicate = (drag: CdkDrag, drop: CdkDropList) => {
return this.isDropAllowed(drag, drop);
};
constructor(
public dragDropService: DragDropService
) {}
ngOnInit(): void {}
ngAfterViewInit(): void {
if (this.dropList) {
this.dragDropService.register(this.dropList);
}
}
dropped(event: CdkDragDrop<IFormControl[]>) {
// Your drop logic
}
isDropAllowed(drag: CdkDrag, drop: CdkDropList) {
if (this.dragDropService.currentHoverDropListId == null) {
return true;
}
return drop.id === this.dragDropService.currentHoverDropListId;
}
dragMoved(event: CdkDragMove<IFormControl>) {
this.dragDropService.dragMoved(event);
}
dragReleased(event: CdkDragRelease) {
this.dragDropService.dragReleased(event);
}
}
- 每当
cdkDrag
移动时,dragMoved
确定正确的 cdkDropList
- 每当
cdkDrag
被释放时,重置确定的cdkDropList
- 最重要的方法是
isDropAllowed
方法,设置为[cdkDropListEnterPredicate]="allowDropPredicate"
到cdkDropList
- 如前所述,cdk material 无法确定正确的下拉列表。
- 如果 cdk 选择了错误的下拉列表,我们只是通过返回 false 不允许 删除。在这种情况下,cdk 会自动选择下一个可能的
cdkDropList
,这是正确的 :)
我使用 CDK Material 拖放实用程序来创建一个支持拖放的表单编辑器。
它工作正常,但在 cdkDropListGroup
中嵌套 cdkDropList
不起作用。
我无法将任何内容拖到嵌套的下拉列表容器中。
<div class="container">
<div class="row" cdkDropListGroup>
<div class="col-2">
<div id="toolbox" cdkDropList>
...
</div>
</div>
<div class="col-10">
<div id="formContainer" cdkDropList>
...
<div class="row">
<div class="col-md-6" cdkDropList>
... column 1 content
</div>
<div class="col-md-6" cdkDropList>
... column 1 content
</div>
</div>
</div>
</div>
</div>
</div>
我花了一些时间,但由于这些帖子的提示,我终于找到了解决方案:
问题是 cdkDropListGroup
不支持嵌套的下拉列表。您需要使用 [cdkDropListConnectedTo]
绑定连接下拉列表。
但是如果您只将列表连接到数组以进行 [cdkDropListConnectedTo]
绑定,则列表顺序会影响放置行为。此外,在嵌套下拉列表中排序将不起作用。
为避免这些问题,您需要创建一个在拖动时寻找正确 cdkDropList
的服务。
export class DragDropService {
dropLists: CdkDropList[] = [];
currentHoverDropListId?: string;
constructor(@Inject(DOCUMENT) private document: Document) {}
public register(dropList: CdkDropList) {
this.dropLists.push(dropList);
}
dragMoved(event: CdkDragMove<IFormControl>) {
let elementFromPoint = this.document.elementFromPoint(
event.pointerPosition.x,
event.pointerPosition.y
);
if (!elementFromPoint) {
this.currentHoverDropListId = undefined;
return;
}
let dropList = elementFromPoint.classList.contains('cdk-drop-list')
? elementFromPoint
: elementFromPoint.closest('.cdk-drop-list');
if (!dropList) {
this.currentHoverDropListId = undefined;
return;
}
this.currentHoverDropListId = dropList.id;
}
dragReleased(event: CdkDragRelease) {
this.currentHoverDropListId = undefined;
}
}
使用的register
向每个cdkDropList
.dropList
数组添加一个新的下拉列表dragMoved
确定鼠标指针下方的正确cdkDropList
。
最好的办法是创建一个包含 cdkDropList
.
以下组件仅用于简单和演示目的。您不应直接使用服务属性。
<div
*ngIf="container"
cdkDropList
[cdkDropListData]="container.controls"
[cdkDropListConnectedTo]="dragDropService.dropLists"
[cdkDropListEnterPredicate]="allowDropPredicate"
(cdkDropListDropped)="dropped($event)"
>
<div
*ngFor="let item of container.controls"
cdkDrag
[cdkDragData]="item"
(cdkDragMoved)="dragMoved($event)"
(cdkDragReleased)="dragReleased($event)"
>
Drag Content
</div>
</div>
export class FormContainerComponent implements OnInit, AfterViewInit {
@ViewChild(CdkDropList) dropList?: CdkDropList;
@Input() container: IFormContainer | undefined;
allowDropPredicate = (drag: CdkDrag, drop: CdkDropList) => {
return this.isDropAllowed(drag, drop);
};
constructor(
public dragDropService: DragDropService
) {}
ngOnInit(): void {}
ngAfterViewInit(): void {
if (this.dropList) {
this.dragDropService.register(this.dropList);
}
}
dropped(event: CdkDragDrop<IFormControl[]>) {
// Your drop logic
}
isDropAllowed(drag: CdkDrag, drop: CdkDropList) {
if (this.dragDropService.currentHoverDropListId == null) {
return true;
}
return drop.id === this.dragDropService.currentHoverDropListId;
}
dragMoved(event: CdkDragMove<IFormControl>) {
this.dragDropService.dragMoved(event);
}
dragReleased(event: CdkDragRelease) {
this.dragDropService.dragReleased(event);
}
}
- 每当
cdkDrag
移动时,dragMoved
确定正确的cdkDropList
- 每当
cdkDrag
被释放时,重置确定的cdkDropList
- 最重要的方法是
isDropAllowed
方法,设置为[cdkDropListEnterPredicate]="allowDropPredicate"
到cdkDropList
- 如前所述,cdk material 无法确定正确的下拉列表。
- 如果 cdk 选择了错误的下拉列表,我们只是通过返回 false 不允许 删除。在这种情况下,cdk 会自动选择下一个可能的
cdkDropList
,这是正确的 :)