简体   繁体   English

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

[英]Angular CDK - issue with scrolling and dragging element inside nested scrollable div

Prerequisite: cdk draggable elements inside a nested scrollable div (see the example https://stackblitz.com/edit/angular-7y19nm?file=app/cdk-drag-drop-sorting-example.html )先决条件:嵌套可滚动 div 内的 cdk 可拖动元素(参见示例https://stackblitz.com/edit/angular-7y19nm?file=app/cdk-drag-drop-sorting-example.html

How to reproduce: Start dragging an item -> scroll the page -> drag item a bit more when not scrolling如何重现:开始拖动项目 -> 滚动页面 -> 不滚动时多拖动项目

Effect: item placeholder stays in wrong place and it's basically impossible to drag item anywhere outside the viewport.效果:项目占位符停留在错误的位置,基本上不可能将项目拖到视口外的任何地方。

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

I've searched for this issue in the Angular components' official Github repository and I have found the following topics:我在Angular 组件的官方 Github 存储库中搜索了这个问题,我发现了以下主题:

There are different solutions depending on the version that you use: Angular 9+ (works also with Angular 10 ) or Angular 8 :根据您使用的版本,有不同的解决方案: Angular 9+ (也适用于Angular 10 )或Angular 8

Angular 9+ (works also Angular 10) Angular 9+(也适用于 Angular 10)

From version 9.1.0 , the scrolling of the parent element is supported by setting the cdkScrollable directive to it.从版本9.1.0 开始,通过设置cdkScrollable指令来支持父元素的滚动。

So, for v9.1.0 and up, the following code should work:因此,对于 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 demo : Stackblitz 演示

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

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


Angular 8角 8

From version 8.1.0 , the scrolling was enabled, but only for the cdkDropList itself or the viewport (for performance reasons).从版本8.1.0 开始,滚动被启用,但适用于cdkDropList 本身视口(出于性能原因)。 So there are two solutions available:所以有两种解决方案:

  1. We can set the fixed height and the overflow: scroll to the cdkDropList element:我们可以设置固定高度和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>

Stackblitz demo : Stackblitz 演示

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

  1. If we can't make the cdkDropList scrollable and there is a parent element that should scroll ( like the situation in the question ), I've adapted a solution found here ( https://github.com/angular/components/issues/16677#issuecomment-562625427 ): we can use a custom directive cdkDropListScrollContainer , that will be set on the cdkDrag elements.如果我们不能使cdkDropList可滚动并且有一个应该滚动的父元素(就像问题中的情况),我已经调整了这里找到的解决方案( https://github.com/angular/components/issues/ 16677#issuecomment-562625427 ):我们可以使用自定义指令cdkDropListScrollContainer ,它将在cdkDrag元素上设置。 This directive will take as a Input the reference to the parent element that should scroll:该指令将把对应该滚动的父元素的引用作为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>

The code for the directive is:该指令的代码是:

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 demo : https://stackblitz.com/edit/angular-jkuqhg Stackblitz 演示https : //stackblitz.com/edit/angular-jkuqhg

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM