MatTable Row Span Drag & Drop Issue

2019-08-27 16:41发布

问题:

I am trying to achieve drag and drop functionality in angular table row but the issue is the table I have has spanned row.When I try to drag and drop the spanned row it just pick up a single row instead of whole spanned row.

I am using https://stackblitz.com/edit/angular-wudscb-kpjdfv to achieve row span in angular table.

app.component.html

<table mat-table [dataSource]="dataSource"
 class="mat-elevation-z8" multiTemplateDataRows
 cdkDropList
[cdkDropListData]="dataSource"
(cdkDropListDropped)="dropTable($event)">

    <ng-container matColumnDef="id">
        <th mat-header-cell *matHeaderCellDef> Priority </th>
        <td mat-cell *matCellDef="let data;let i = dataIndex" [attr.rowspan]="getRowSpan('Priority',i)" [style.display]="getRowSpan('Priority', i) ? '' : 'none'">
         {{ data.id }} </td>
    </ng-container>

    <ng-container matColumnDef="name">
        <th mat-header-cell *matHeaderCellDef> Name </th>
        <td mat-cell *matCellDef="let data;let i = dataIndex" [attr.rowspan]="getRowSpan('Name',i)" [style.display]="getRowSpan('Name', i) ? '' : 'none'">
         {{ data.name }} </td>
    </ng-container>

    <ng-container matColumnDef="weight">
        <th mat-header-cell *matHeaderCellDef> Weight </th>
        <td mat-cell *matCellDef="let data;let i = dataIndex" [attr.rowspan]="getRowSpan('Weight',i)" [style.display]="getRowSpan('Weight', i) ? '' : 'none'">
         {{ data.weight }} </td>
    </ng-container>

    <ng-container matColumnDef="descriptions">
        <th mat-header-cell *matHeaderCellDef> Descriptions </th>
        <td mat-cell *matCellDef="let data"> {{ data.descriptions }} </td>
    </ng-container>


  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;" cdkDrag [cdkDragData]="row"></tr>



</table>

app.component.ts

import { Component , ViewChild } from '@angular/core';
import {CdkDragDrop, moveItemInArray, transferArrayItem, CdkDragHandle} from '@angular/cdk/drag-drop';
import { MatTable } from '@angular/material';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  @ViewChild('table') table: MatTable<PeriodicElement>;
  dataSource = ELEMENT_DATA;


  displayedColumns = ['id', 'name', 'weight', 'descriptions'];

  spans = [];

  tempRowId = null;
  tempRowCount = null;

  constructor() {
    this.cacheSpan('Priority', d => d.id);
    this.cacheSpan('Name', d => d.name);
    this.cacheSpan('Weight', d => d.weight);
  }

  /**
   * Evaluated and store an evaluation of the rowspan for each row.
   * The key determines the column it affects, and the accessor determines the
   * value that should be checked for spanning.
   */
  cacheSpan(key, accessor) {
    for (let i = 0; i < DATA.length;) {
      let currentValue = accessor(DATA[i]);
      let count = 1;

      // Iterate through the remaining rows to see how many match
      // the current value as retrieved through the accessor.
      for (let j = i + 1; j < DATA.length; j++) {
        if (currentValue != accessor(DATA[j])) {
          break;
        }

        count++;
      }

      if (!this.spans[i]) {
        this.spans[i] = {};
      }

      // Store the number of similar values that were found (the span)
      // and skip i to the next unique row.
      this.spans[i][key] = count;
      i += count;
    }
  }

  getRowSpan(col, index) {    
    return this.spans[index] && this.spans[index][col];
  }


  dropTable(event: CdkDragDrop<PeriodicElement[]>) {
    const prevIndex = this.dataSource.findIndex((d) => d === event.item.data);
    moveItemInArray(this.dataSource, prevIndex, event.currentIndex);
    this.table.renderRows();
  }

}

export interface PeriodicElement {
  id: number;
  name: string;
  weight: number;
  descriptions: string[];
}

const originalData = [
  { id: 1, name: 'Hydrogen', weight: 1.0079, descriptions: ['row1', 'row2'] },
  { id: 2, name: 'Helium', weight: 4.0026, descriptions: ['row1', 'row2', 'row3'] },
  { id: 3, name: 'Lithium', weight: 6.941, descriptions: ['row1', 'row2'] },
  { id: 4, name: 'Beryllium', weight: 9.0122, descriptions: ['row1', 'row2', 'row3'] },
  { id: 5, name: 'Boron', weight: 10.811, descriptions: ['row1'] },
  { id: 6, name: 'Carbon', weight: 12.0107, descriptions: ['row1', 'row2', 'row3'] },
  { id: 7, name: 'Nitrogen', weight: 14.0067, descriptions: ['row1'] },
  { id: 8, name: 'Oxygen', weight: 15.9994, descriptions: ['row1'] },
  { id: 9, name: 'Fluorine', weight: 18.9984, descriptions: ['row1', 'row2', 'row3'] },
  { id: 10, name: 'Neon', weight: 20.1797, descriptions: ['row1', 'row2', 'row3'] },
]

const DATA = originalData.reduce((current, next) => {
  next.descriptions.forEach(b => {
    current.push({ id: next.id, name: next.name, weight: next.weight, descriptions: b })
  });
  return current;
}, []);
console.log(DATA)

const ELEMENT_DATA: PeriodicElement[] = DATA;

I want the whole spanned row to be able to drag and drop to other place.