CSS flexbox中的Angular CDK拖放问题

Posted

技术标签:

【中文标题】CSS flexbox中的Angular CDK拖放问题【英文标题】:Angular CDK drag and drop issue inside CSS flexbox 【发布时间】:2019-06-18 08:16:32 【问题描述】:

我在使用 Angular CDK 中的拖放模块时遇到了问题。我在一个容器 div 中使用它,它具有(除其他外)以下 CSS 属性:

display: flex;
flex-wrap: wrap;

flex_wrap 属性在这里,如果包含的可拖动元素不适合容器,它们会换行到第二行,依此类推。

由于拖动是水平的 (cdkDropListOrientation="horizontal"),当所有元素都放在一行中时,这可以正常工作,但是一旦它们换行到第二行,拖放就会变得有问题。我做了以下 stackblitz 来重现错误:https://stackblitz.com/edit/angular-fytgp6。

如果有人知道如何解决此问题或考虑解决此问题的方法,那将有很大帮助!

【问题讨论】:

您找到解决方案了吗? 【参考方案1】:

这是 CDK 拖放的一个已知问题:https://github.com/angular/material2/issues/13372

本质上,您需要有一个定义为“cdkDropListGroup”的父 div,然后除了具有“cdkDrag”属性之外,您还需要将每个可拖动项视为“cdkDropList”。这应该使每个项目都是它自己的容器,并且“cdkDropListGroup”指令将它们连接在一起。

然后,您可以在 cdkDropList 容器上使用 *ngFor 来为每个数组项生成一个。将 [cdkDropListData]="index" 与 cdkDropList 一起放置,以便您可以将当前拖动的索引传输到 cdkDrag。使用子 cdkDrag 元素,您可以使用 [cdkDragData]="index" 获取此索引。然后,在 cdkDrag 子节点上有一个事件绑定 (cdkDragEntered)="entered($event)",每次您尝试将元素拖动到新容器之一时都会触发该事件绑定。在输入的函数中,使用 CDK 中的 moveItemInArray 方法来传输项目。

entered(event: CdkDragEnter) 
  moveItemInArray(this.items, event.item.data, event.container.data);
<div style="display:flex;flex-wrap:wrap" cdkDropListGroup>
  <div cdkDropList [cdkDropListData]="i" *ngFor="let item of items; let i = index;"  [style.width]="item.width || '100%'">
    <div cdkDrag [cdkDragData]="i" (cdkDragEntered)="entered($event)">
      item
    </div>
  </div>
</div>

如果这对您不起作用,那么您可以尝试使用 mat-grid 来控制您的布局。

<mat-grid-list cdkDropListGroup>
  <mat-grid-tile cdkDropList [cdkDropListData]="i" *ngFor="let item of items; let i = index;" [colspan]="item.cols" [rowspan]="item.rows">
    <div cdkDrag [cdkDragData]="i" (cdkDragEntered)="entered($event)"> 
      item
    </div> 
  </mat-grid-tile> 
</mat-grid-list>

【讨论】:

嗨,杰夫。您有任何可以使用 mat-grid 解决此问题的教程/材料吗? 嗨,legin,它应该像摆脱 display:flex 一样简单;和 div,而是使用 标签。我在答案中添加了一个示例。这假定您的每个项目都有一个“cols”和“rows”属性来确定每个项目应该跨越的列数和行数。 我试图实现这一点,并且 cdkDropList div 正在按预期切换位置,但是,我遇到了一个问题,即占位符保留在它被拖到的 cdkDropList 内,即使它已经切换了位置,并且里面的 cdkDrag 和被拖到里面的新 cdkDrag 的占位符都占据同一个 cdkDropList 直到它被丢弃。 嗨,威利,听起来您需要向各种容器添加一些 CSS 来控制放置占位符的外观以及拖动时框的外观。 Angular Material 文档在 material.angular.io/cdk/drag-drop/overview 有一些示例 CSS。您可以在“自定义拖动预览/占位符”下看到它。 感谢@JeffGilliland,这对于单个列表来说效果很好。我想将盒子从一个列表拖到另一个列表,这是使用 transferArrayItem 完成的,当我们使用 flex-wrap:wrap 时我该怎么做【参考方案2】:

我已经使用 Angular 的工具包(cdkDropListGroup、moveItemInArray 和 transferArrayItem)实现了一个简单的解决方案:https://stackblitz.com/edit/angular-drag-n-drop-mixed-orientation-example

我所做的只是创建一个项目表矩阵,用作模板的视图模型,并且组件在项目列表(输入模型)与表(视图模型)之间同步。我在这里贴了详细的解释:https://taitruong.github.io/software-developer.org/post/2019/10/26/Angular-drag'n'drop-mixed-orientation-in-flex-row-wrap/

模板:

<div #tableElement cdkDropListGroup>
  <!-- Based on the width of template reference #tableElement' and item box width,
       columns per row can be calculated and a items table matrix is initialized-->
  <div
    fxLayout="row"
    *ngFor="let itemsRow of getItemsTable(tableElement)"
    cdkDropList
    cdkDropListOrientation="horizontal"
    [cdkDropListData]="itemsRow"
    (cdkDropListDropped)="reorderDroppedItem($event)"
  >
    <!-- Component.reorderDroppedItem():
         reorders table/view model, update input model, and resize table matrix-->
    <div *ngFor="let item of itemsRow" cdkDrag>
      <div class="drag-placeholder" *cdkDragPlaceholder></div>
      <div fxLayoutAlign="center center" class="item-box"> item </div>
    </div>
  </div>
</div>

CSS:

.item-box 
  width: 150px;
  height: 150px;
  border: solid 3px #ccc;
  background: #fff;
  font-size: 30pt;
  font-weight: bold;
  border-radius: 5px;
  margin: 0px 0px 5px 5px;


 .drag-placeholder 
   background: #ccc;
   border: dotted 3px #999;
   height: 150px;
   width: 50px;
   transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
 

组件:

export class AppComponent 
  // one dimensional input model
  items: Array<number> = Array.from( length: 21 , (v, k) => k + 1);
  // two dimensional table matrix representing view model
  itemsTable: Array<number[]>;

  // fix column width as defined in CSS (150px + 5px margin)
  boxWidth = 155;
  // calculated based on dynamic row width
  columnSize: number;

  getItemsTable(tableElement: Element): number[][] 
    // calculate column size per row
    const  width  = tableElement.getBoundingClientRect();
    const columnSize = Math.round(width / this.boxWidth);
    // view has been resized? => update table with new column size
    if (columnSize != this.columnSize) 
      this.columnSize = columnSize;
      this.initTable();
    
    return this.itemsTable;
  

  initTable() 
    // create table rows based on input list
    // example: [1,2,3,4,5,6] => [ [1,2,3], [4,5,6] ]
    this.itemsTable = this.items
      .filter((_, outerIndex) => outerIndex % this.columnSize == 0) // create outter list of rows
      .map((
        _,
        rowIndex // fill each row from...
      ) =>
        this.items.slice(
          rowIndex * this.columnSize, // ... row start and
          rowIndex * this.columnSize + this.columnSize // ...row end
        )
      );
  

  reorderDroppedItem(event: CdkDragDrop<number[]>) 
    // same row/container? => move item in same row
    if (event.previousContainer === event.container) 
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
     else 
      // different rows? => transfer item from one to another list
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    

    // update items after drop: flatten matrix into list
    // example: [ [1,2,3], [4,5,6] ] => [1,2,3,4,5,6]
    this.items = this.itemsTable.reduce(
      (previous, current) => previous.concat(current),
      []
    );

    // re-initialize table - makes sure each row has same numbers of entries
    // example: [ [1,2], [3,4,5,6] ] => [ [1,2,3], [4,5,6] ]
    this.initTable();
  

【讨论】:

你能修复文章的链接吗? 文章好像被删除了。当我删除 &lt;div class="drag-placeholder" *cdkDragPlaceholder&gt;&lt;/div&gt; 时,它正常工作(在 Angular 13 中)

以上是关于CSS flexbox中的Angular CDK拖放问题的主要内容,如果未能解决你的问题,请参考以下文章

Angular CDK拖放 - 如何保持原始位置?

重新实例化数据时,Angular CDK 拖放列表会中断

如何在 Angular 中禁用或覆盖 cdk-focused

Angular CDK 拖放:不要移动源项目

CDK 拖放在移动富卡 div 角度时仅显示文本数据

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