web-dev-qa-db-ja.com

Angular CSS内のCDKドラッグアンドドロップの問題flexbox

Angular CDKからドラッグアンドドロップモジュールを使用して問題が発生しました。(とりわけ)次のCSSプロパティを持つコンテナdiv内で使用しています:

display: flex;
flex-wrap: wrap;

flex_wrapプロパティはここにあるので、含まれているドラッグ可能な要素がコンテナに収まらない場合、それらは2行目に折り返されます。

ドラッグは水平なので(cdkDropListOrientation="horizontal")、これはすべての要素が1行に収まる場合は正常に機能しますが、2行目に折り返されるとすぐに、ドラッグアンドドロップがバグになります。エラーを再現するために、次のstackblitzを作成しました: https://stackblitz.com/edit/angular-fytgp6

誰かがこの問題を修正する方法を知っているか、これの回避策を考えているなら、それは大きな助けになるでしょう!

3
Paul Selle

これはCDKドラッグアンドドロップの既知の問題です: https://github.com/angular/material2/issues/13372

基本的に、「cdkDropListGroup」として定義されている親divが必要です。次に、「cdkDrag」プロパティに加えて、ドラッグ可能な各アイテムを「cdkDropList」として扱う必要があります。これにより、各アイテムが独自のコンテナになり、「cdkDropListGroup」ディレクティブがそれらをすべて接続するようになります。

次に、cdkDropListコンテナに* ngForを設定して、配列アイテムごとに1つ生成できます。 [cdkDropListData] = "index"をcdkDropListと一緒に配置して、現在ドラッグしているインデックスをcdkDragに転送できるようにします。子cdkDrag要素を使用すると、[cdkDragData] = "index"でこのインデックスを取得できます。次に、cdkDrag子にイベントバインディング(cdkDragEntered)= "entered($ event)"を設定します。これは、要素を新しいコンテナの1つにドラッグしようとするたびに起動します。入力した関数内で、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-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>
4
Jeff Gilliland

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();
  }
}
1
Tai Truong