Angular 7 拖放 - 动态创建拖放区

Posted

技术标签:

【中文标题】Angular 7 拖放 - 动态创建拖放区【英文标题】:Angular 7 Drag and Drop - Dynamically Create Drop Zones 【发布时间】:2019-04-13 19:29:37 【问题描述】:

有没有办法动态创建拖放区?我在使用 ngFor 和 cdkDropList 时遇到了一些问题。

这是我的第一个列表和可拖动元素:

       <div class="subj-container" 
        cdkDropListOrientation="horizontal" 
        cdkDropList 
        #subjectList="cdkDropList"
        [cdkDropListData]="subjects"  
        [cdkDropListConnectedTo]="[lessonList]" 
        (cdkDropListDropped)="drop($event)"
        >
            <div class="subject" *ngFor="let subject of subjects" cdkDrag>
                subject.name
            </div>
        </div>

这是我的第二个清单:

          <div class="conta" cdkDropList
                #lessonList="cdkDropList"
                [cdkDropListData]="appointment.lessons"
                [cdkDropListConnectedTo]="[subjectList]"
                (cdkDropListDropped)="drop($event)">
                    <div class="sub" cdkDrag *ngFor="let lesson of appointment.lessons">
                        lesson.name
                </div>
           </div>

现在,具有“conta”类的 div 位于 *ngFor 内。

我想我的问题是我的第二个列表。如果我将元素从第二个列表拖动到列表一,它可以正常工作,但是如果我尝试将元素从列表一拖动到第二个列表中的任何列表实例,它无法识别该元素正在被拖动。演示在这里:

我在这里做错了吗? 打字稿部分工作正常。

谢谢

【问题讨论】:

如果您删除使其成为多行的样式,您的第二个列表是否有效?下拉列表只能是水平的或垂直的。您在这里似乎有一个网格,它不起作用,因为存在依赖于知道是否计算下拉列表元素在 x 或 y 维度中的相对距离的内在逻辑。 @Lightheaded - 是的,我确实想到了这一点,并删除了所有样式,但没有。我找到了解决方案。 cdkDropListConnectedTo 出现问题。它连接到null,所以我做了我的解决方法。在下面检查我的答案,谢谢! 你有这方面的工作示例吗? 【参考方案1】:

经过一整天的研究,我在 Github 上的 Angular CDK 存储库中找到了 this 拉取请求。现在,由于我不知道如何将 cdkDropListGroup 集成到我的示例中,我决定创建一个 ID 数组,将其添加到 [cdkDropListConnectedTo]

我的第二个列表的每个实例都会生成 ID,并且该 ID 将添加到具有合适前缀的数组中(在我的第二个列表中,在 cdkDropList 上):

<div cdkDropList
      [attr.id]="addId(i, j)"
      [cdkDropListData]="appointment.lessons"
      [cdkDropListConnectedTo]="[subjectList]"
      (cdkDropListDropped)="drop($event)"
>

addId方法:

addId(i, j) 
    this.LIST_IDS.push('cdk-drop-list-' + i + '' + j);
    return i + '' + j;

(cdk-drop-list- 是一个 ID 前缀。CDK 将此前缀放在每个具有 cdkDropList 属性的元素上)

所以,我的数组将如下所示:

cdk-drop-list-00 cdk-drop-list-01 cdk-drop-list-02 等

现在,我将该数组传递给我的第一个列表中的 [cdkDropListConnectedTo]

<div class="subj-container" 
    cdkDropListOrientation="horizontal"
    cdkDropList 
    #subjectList="cdkDropList"            
    [cdkDropListData]="subjects" 
    [cdkDropListConnectedTo]="LIST_IDS"
    (cdkDropListDropped)="drop($event)"
>

而且它完美无瑕!

希望这将帮助任何有同样问题的人。另外,看看我提到的拉取请求,我的解决方案只是一种解决方法,cdkDropListGroup

可能有更好的解决方案

【讨论】:

cdkDropListGroup 尚未发布。留意下一个版本。同时,我一直在使用相同的方法——使用列表 ID 的映射。目前似乎没有更好的方法来做到这一点。每当您提到的功能发布时,您都可以删除所有与 ID 混淆的 hacky :) cdkDropListGroup 已发布。我可以在任何地方使用 cdkDropListGroup 获得这个演示吗? 我在 StackBlitz 上添加了一个带有 cdkDropListGroup 的演示 stackblitz.com/edit/angular-a4ftm7 我也试图实现同样的目标。任何人都可以帮助我。请找到链接。 ***.com/questions/59386696/… 该方法适用于适合对话框的行。但是,当有太多行无法容纳在可见区域时,拖放无法确定当前索引。如果我选择最后一行,并尝试通过垂直滚动条将其移至顶部,则失败。有解决问题的办法吗?【参考方案2】:

来源Link

演示Link

对于Dynamic Drag n Drop Lists,我们可以使用ID代替#Template variables

app.component.html

<div class="col-md-3" *ngFor="let week of weeks">
  <div class="drag-container">
    <div class="section-heading">Week week.id</div>

    <div cdkDropList id="week.id" [cdkDropListData]="week.weeklist"
      [cdkDropListConnectedTo]="connectedTo" class="item-list" (cdkDropListDropped)="drop($event)">
      <div class="item-box" *ngFor="let weekItem of week.weeklist" cdkDrag>Week week.id weekItem</div>
    </div>
  </div>
</div>

app.component.ts

import  Component  from '@angular/core';
import  CdkDragDrop, moveItemInArray, transferArrayItem  from '@angular/cdk/drag-drop';

@Component(
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
)
export class AppComponent 

  weeks = [];
  connectedTo = [];


  constructor() 
    this.weeks = [
      
        id: 'week-1',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      , 
        id: 'week-2',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      , 
        id: 'week-3',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      , 
        id: 'week-4',
        weeklist: [
          "item 1",
          "item 2",
          "item 3",
          "item 4",
          "item 5"
        ]
      ,
    ];
    for (let week of this.weeks) 
      this.connectedTo.push(week.id);
    ;
  

  drop(event: CdkDragDrop<string[]>) 
    if (event.previousContainer === event.container) 
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
     else 
      transferArrayItem(event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex);
    
  

【讨论】:

【参考方案3】:

使用 cdkDropListGroup 您现在可以:

<div cdkDropListGroup>

  <div cdkDropList
    [cdkDropListData]="data"
    (cdkDropListDropped)="drop($event)">
    <div class="row m-2">
        <div *ngFor="let i of data" cdkDrag>i</div>          
    </div>
  </div>

  <div class="subj-container" 
    cdkDropListOrientation="horizontal"
    cdkDropList 
    #subjectList="cdkDropList"            
    [cdkDropListData]="subjects" 
    (cdkDropListDropped)="drop($event)"> 
  </div>

</div>

在这种情况下不再需要 cdkDropListConnectedTo。 见https://github.com/angular/material2/blob/master/src/cdk/drag-drop/drag-drop.md

【讨论】:

【参考方案4】:

我也不得不面对这个问题。我尝试了 id 方法,但在使用时我并没有感到太自信。当我在这个 addId() 函数中 console.log 时,我可以看到同一个 id 重复了几次。 取而代之的是,我尝试使用 @ViewChildren 装饰器来实时拥有 cdkList 组件,它对我来说效果很好。

在打字稿中

  cdkDropTrackLists: CdkDropList[];
  @ViewChildren(CdkDropList)
  set cdkDropLists(value: QueryList<CdkDropList>) 
    this.cdkDropTrackLists = value.toArray();
  

在模板中

<div
        cdkDropList
        class="track-list"
        cdkDropListSortingDisabled
        [cdkDropListData]="paragraphIdentifiers"
        (cdkDropListDropped)="drop($event)"
        [cdkDropListConnectedTo]="cdkDropTrackLists">
</div>

我认为我可以改进它,而 cdkDropLists 作为 QueryList 具有可观察的更改属性。

【讨论】:

【参考方案5】:

我使用一组动态列表和 cdkDropListGroup 构建了一个stackblitz example。我正在记住列表中项目的第一个列表的名称。通过记住第一个列表,我可以跟踪哪些对象已更改,这对于构建某些案例非常方便。它还让我可以更改已移动项目的背景颜色,清楚地显示更改的内容。

【讨论】:

【参考方案6】:

有点晚了,但很有帮助,值得分享。 我使用角材料版本 10 创建了一个看板。 这是演示的链接。 https://stackblitz.com/edit/angular-material-kanban

【讨论】:

以上是关于Angular 7 拖放 - 动态创建拖放区的主要内容,如果未能解决你的问题,请参考以下文章

Angular2拖放目标

Angular 7 - 使用父可放置容器拖放

整个页面作为拖放区进行拖放

HTML5 拖放 - 仅在每个拖放区的项目上

如何使用angularJS突出显示可拖动元素下的拖放区?

上传文件时检测到取消的拖放操作