首页 > 解决方案 > Angular 嵌套拖放 / CDK 材质 cdkDropListGroup cdkDropList nested

问题描述

我使用 CDK Material 拖放实用程序来创建一个启用拖放功能的表单编辑器。

cdkDropList它工作正常,但在 a中嵌套 acdkDropListGroup不起作用。我无法将任何内容拖到嵌套的下拉列表容器中。

<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>

拖放不起作用

标签: angularangular-materialdrag-and-dropangular-cdk

解决方案


我花了一些时间,但由于该帖子的提示,我终于找到了解决方案:

问题是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将一个新的下拉列表添加到dropList每个cdkDropList.

  • 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移动 a 时,dragMoved确定正确的cdkDropList
  • 每当 acdkDrag被释放时,重置确定的cdkDropList
  • 最重要的方法isDropAllowed是设置[cdkDropListEnterPredicate]="allowDropPredicate"的方法cdkDropList
    • 如前所述,cdk 材料无法确定正确的下拉列表。
    • 如果 cdk 选择了错误的下拉列表,我们只是通过返回 false 来禁止下拉列表。在这种情况下,cdk 会自动选择下一个可能cdkDropList是正确的 :)

拖放嵌套工作


推荐阅读