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

时间:2019-09-02 10:11:57

标签: javascript html 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>

1 个答案:

答案 0 :(得分:0)

我已经在Angular components' official Github repository中搜索了此问题,并且发现了以下主题:

根据您使用的版本,有不同的解决方案: 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>

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: scroll设置为cdkDropList元素:
<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>

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>

该指令的代码为:

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;
      }
    });

  }
}

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