Angular CDK-嵌套可滚动div中滚动和拖动元素的问题

use*_*124 5 html javascript css angular angular-cdk

先决条件:嵌套可滚动div中的cdk可拖动元素(请参见示例https://stackblitz.com/edit/angular-7y19nm?file=app/cdk-drag-drop-sorting-example.html

如何复制:开始拖动项目->滚动页面->不滚动时再拖动项目一点

效果:项目占位符停留在错误的位置,基本上不可能将项目拖到视口之外的任何位置。

<div style="height: 100vh; overflow-y: auto">
  <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
    <div class="example-box" *ngFor="let movie of movies" cdkDrag>{{movie}}</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

and*_*tor 7

我在Angular 组件的官方 Github 存储库中搜索了这个问题,我发现了以下主题:

根据您使用的版本,有不同的解决方案:Angular 9+(也适用于Angular 10)或Angular 8

Angular 9+(也适用于 Angular 10)

从版本9.1.0 开始,通过cdkScrollable为其设置指令来支持父元素的滚动。

因此,对于 v9.1.0 及更高版本,以下代码应该可以工作:

<div style="height: 100vh; overflow-y: auto" cdkScrollable>
  <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
    <div class="example-box" *ngFor="let movie of movies" cdkDrag>{{movie}}</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

Stackblitz 演示

https://stackblitz.com/edit/angular-swaqkk-yjiz7r使用 v10.0.1

https://stackblitz.com/edit/angular-vszdat使用 v9.2.4


角 8

从版本8.1.0 开始,滚动被启用,但适用于cdkDropList 本身视口(出于性能原因)。所以有两种解决方案:

  1. 我们可以设置固定的高度和overflow: scrollcdkDropList元素:
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)" style="height: 100vh; overflow-y: auto">
  <div class="example-box" *ngFor="let movie of movies" cdkDrag>{{movie}} 
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

Stackblitz 演示

https://stackblitz.com/edit/angular-avezy6

  1. 如果我们不能cdkDropList滚动并且有一个应该滚动的父元素(就像问题中的情况),我已经调整了一个在这里找到的解决方案(https://github.com/angular/components/issues/16677 #issuecomment-562625427 ):我们可以使用自定义指令cdkDropListScrollContainer,它将在cdkDrag元素上设置。该指令将作为Input对应该滚动的父元素的引用:
<div class="example-container" style="height: 500px; overflow-y: auto" #scrollContainer>
  <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
    <div
      class="example-box"
      *ngFor="let movie of movies"
      cdkDrag
      [cdkDropListScrollContainer]="scrollContainer">
      {{movie}}
    </div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

该指令的代码是:

import { Directive, Input, ElementRef } from "@angular/core";
import { CdkDrag } from "@angular/cdk/drag-drop";

@Directive({
  selector: "[cdkDropListScrollContainer]"
})
export class CdkDropListScrollContainerDirective {
  @Input("cdkDropListScrollContainer") scrollContainer: HTMLElement;
  originalElement: ElementRef<HTMLElement>;

  constructor(cdkDrag: CdkDrag) {

    cdkDrag._dragRef.beforeStarted.subscribe(() => {
      const cdkDropList = cdkDrag.dropContainer;
      if (!this.originalElement) {
        this.originalElement = cdkDropList.element;
      }

      if (this.scrollContainer) {
        const element = this.scrollContainer;
        cdkDropList._dropListRef.element = element;
        cdkDropList.element = new ElementRef<HTMLElement>(element);
      } else {
        cdkDropList._dropListRef.element = cdkDropList.element.nativeElement;
        cdkDropList.element = this.originalElement;
      }
    });

  }
}

Run Code Online (Sandbox Code Playgroud)

Stackblitz 演示https : //stackblitz.com/edit/angular-jkuqhg