Drag and drop the expanded rows into another set of expanded rows - Angular Material

Discussão em 'Angular' iniciado por developer8492, Agosto 16, 2025.

  1. I want to have the ability to drag and dro one of the rows that expand in the table to another set of rows that expand in the table. I have added drag and drop properties in the html for the expanded rows (expandedDetail) by using cdkDrag but that isn't working. I am asking for assistance from anyone that is able to help me accomplish this. I am not sure what I'm missing from my code to be able to accomplish this.

    html

    <button (click)="toggleExpandAllElements()">
    {{ !isExpandAll ? 'Expand' : 'Collapse' }} All
    </button>
    <button>Undo Changes</button>
    <button>Logs</button>
    <button>Add/Remove Reps</button>
    <button>Report</button>
    <br />
    <br />

    <!-- {{ displayedColumns | json }} -->
    <table
    mat-table
    [dataSource]="dataSource"
    multiTemplateDataRows
    class="mat-elevation-z8"
    cdkDropListGroup
    >
    <!-- toggle columns menu -->
    <ng-container matColumnDef="{{toggleColDef}}">
    <th mat-header-cell *matHeaderCellDef>
    <button mat-button [matMenuTriggerFor]="menu" class="table-config-menu">
    <mat-icon aria-hidden="false" aria-label="Example home icon"
    >build</mat-icon
    >
    <mat-menu #menu="matMenu">
    <span class="table-config-menu-label">Edit Columns</span>
    <div
    class="table-config-menu-options"
    style="display: flex; flex-direction: column; padding: 8px"
    >
    <mat-checkbox
    *ngFor="let cd of columnDefinitions; let i = index"
    [checked]="isColumnVisible(cd.def)"
    (change)="toggleColumn(cd.def, $event.checked)"
    >
    {{cd.label}}
    </mat-checkbox>
    </div>
    </mat-menu>
    </button>
    </th>
    <td mat-cell *matCellDef="let element"></td>
    </ng-container>

    <ng-container
    matColumnDef="{{column}}"
    *ngFor="let column of columnsToDisplay; let i = index; trackBy column"
    >
    <ng-container>
    <th
    mat-header-cell
    *matHeaderCellDef
    cdkDropList
    cdkDropListLockAxis="x"
    cdkDropListOrientation="horizontal"
    (cdkDropListDropped)="dropListDropped($event, column)"
    cdkDrag
    (cdkDragStarted)="dragStarted($event, i)"
    [cdkDragData]="{name: column, columnIndex: i}"
    >
    {{column}}
    </th>
    <td mat-cell *matCellDef="let element">{{element[column]}}</td>
    </ng-container>
    </ng-container>
    <ng-container matColumnDef="action">
    <th mat-header-cell *matHeaderCellDef>Actions</th>
    <td mat-cell *matCellDef="let element" class="checkbox-spacing">
    <mat-icon (click)="toggleExpandElement(element)"
    >{{isElementExpanded(element) ? 'expand_less' :
    'expand_more'}}</mat-icon
    >
    </td>
    </ng-container>
    <ng-container matColumnDef="select">
    <th mat-header-cell *matHeaderCellDef>
    <mat-checkbox
    (change)="$event ? toggleAllRows() : null"
    [checked]="selection.hasValue() && isAllSelected()"
    [indeterminate]="selection.hasValue() && !isAllSelected()"
    [aria-label]="checkboxLabel()"
    >
    </mat-checkbox>
    </th>
    <td mat-cell *matCellDef="let row">
    <mat-checkbox
    (click)="$event.stopPropagation()"
    (change)="$event ? selection.toggle(row) : null"
    [checked]="selection.isSelected(row)"
    [aria-label]="checkboxLabel(row)"
    >
    </mat-checkbox>
    </td>
    </ng-container>

    <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->

    <ng-container matColumnDef="expandedDetail">
    <td
    mat-cell
    *matCellDef="let element"
    [attr.colspan]="columnsToDisplayWithExpand.length"
    >
    <div
    class="example-element-detail"
    [@detailExpand]="isElementExpanded(element) ? 'expanded' : 'collapsed'"
    >
    <div></div>
    <!-- <div class="example-element-diagram">
    <div class="example-element-position">{{element.position}}</div>
    <div class="example-element-symbol">{{element.symbol}}</div>
    <div class="example-element-name">{{element.name}}</div>
    <div class="example-element-weight">{{element.weight}}</div>
    </div>
    <div class="example-element-description">
    {{element.description}}
    <span class="example-element-description-attribution">
    -- Wikipedia
    </span>
    </div> -->
    <div class="example-element-name expanded-column-spacing">
    {{element.Name}}
    </div>
    <div class="example-element-weight expanded-column-spacing">
    {{element.weight}}
    </div>
    <div class="example-element-weight expanded-column-spacing">
    {{element.weight}}
    </div>
    </div>
    </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr
    mat-row
    *matRowDef="let element; columns: displayedColumns;"
    class="example-element-row"
    [class.example-expanded-row]="isElementExpanded(element)"
    ></tr>
    <tr
    mat-row
    *matRowDef="let row; columns: ['expandedDetail']"
    (click)="toggle(row)"
    cdkDrag
    cdkDragLockAxis="y"
    [cdkDragData]="row" ></tr>
    <tr
    mat-row
    *matRowDef="let row; columns: ['expandedDetail']"
    class="example-detail-row"
    ></tr>
    <tr
    mat-row
    *matRowDef="let row; columns: ['expandedDetail']"
    class="example-detail-row"
    ></tr>
    <tr
    mat-row
    *matRowDef="let row; columns: ['expandedDetail']"
    class="example-detail-row"
    ></tr>
    </tr>
    <tr mat-row *matRowDef="let row; columns: getDisplayedColumns()"></tr>
    </table>


    ts

    import { SelectionModel } from '@angular/cdk/collections';
    import { MatTableDataSource } from '@angular/material/table';
    import { Component } from '@angular/core';

    import {
    CdkDragStart,
    CdkDropList,
    CdkDragDrop,
    moveItemInArray,
    } from '@angular/cdk/drag-drop';
    import {
    animate,
    state,
    style,
    transition,
    trigger,
    } from '@angular/animations';

    /**
    * @title Table with expandable rows
    */
    @Component({
    selector: 'table-expandable-rows-example',
    styleUrls: ['table-expandable-rows-example.scss'],
    templateUrl: 'table-expandable-rows-example.html',
    animations: [
    trigger('detailExpand', [
    state('collapsed', style({ height: '0px', minHeight: '0' })),
    state('expanded', style({ height: '*' })),
    transition(
    'expanded <=> collapsed',
    animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')
    ),
    ]),
    ],
    })
    export class TableExpandableRowsExample {
    columnsToDisplay: string[] = [
    'Name',
    'weight',
    'symbol',
    'position',
    ];

    columns: any[] = ['Name', 'weight', 'symbol', 'position'];
    columnsToDisplayWithExpand = ['select', 'action', ...this.columnsToDisplay];

    dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
    expandedElement: PeriodicElement | null;
    selection = new SelectionModel<PeriodicElement>(true, []);

    expandedElements: PeriodicElement[] | null = [];

    // drop(event: CdkDragDrop<string[]>) {
    // moveItemInArray(
    // this.columnsToReorder,
    // event.previousIndex,
    // event.currentIndex
    // );
    // }
    previousIndex: number;

    setDisplayedColumns() {
    this.displayedColumns = [
    'select',
    'action',
    ...this.columns,
    this.toggleColDef,
    ];
    }

    dragStarted(event: CdkDragStart, index: number) {
    const prevIndex = this.columns.indexOf(event.source.data.name);
    this.previousIndex = prevIndex;
    }

    dropListDropped(
    event: CdkDragDrop<any>,
    targetColumn: string
    ) {
    if (event) {
    const dropIndex = this.columns.indexOf(targetColumn);

    console.log(
    `${event.item.data.name} Move from ${this.previousIndex} to ${dropIndex}`
    );

    moveItemInArray(this.columns, this.previousIndex, dropIndex);

    this.setDisplayedColumns();
    }
    }
    /** Whether the number of selected elements matches the total number of rows. */
    isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected === numRows;
    }

    /** Selects all rows if they are not all selected; otherwise clear selection. */
    toggleAllRows() {
    if (this.isAllSelected()) {
    this.selection.clear();
    return;
    }

    this.selection.select(...this.dataSource.data);
    }

    /** The label for the checkbox on the passed row */
    checkboxLabel(row?: PeriodicElement): string {
    if (!row) {
    return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
    }
    return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${
    row.position + 1
    }`;
    }
    columnDefinitions = [
    { def: 'Name', label: 'Name', visible: true },
    {
    def: 'weight',
    label: 'Weight',
    visible: true,
    },
    {
    def: 'symbol',
    label: 'Symbol',
    visible: true,
    },
    {
    def: 'position',
    label: 'Position',
    visible: true,
    },
    ];

    toggleColDef = 'toggleCol';

    displayedColumns: any = [
    ...this.columnsToDisplayWithExpand,
    this.toggleColDef,
    ];

    // all columns for tracking order
    allCols = [...this.displayedColumns];

    // use Set to track checked columns
    checkedColumns = new Set(this.displayedColumns);

    toggleColumn(col: string, checked: boolean) {
    if (checked) {
    // add to checked columns if checked
    this.checkedColumns.add(col);
    } else {
    // remove
    this.checkedColumns.delete(col);
    }

    // recreate columns in the same order, add toggle manually
    this.displayedColumns = this.allCols.filter((el) => {
    if (el === this.toggleColDef) {
    return true;
    } else {
    return this.checkedColumns.has(el);
    }
    });
    }

    isColumnVisible(col: string): boolean {
    return this.checkedColumns.has(col);
    }

    getDisplayedColumns(): string[] {
    return this.columnDefinitions
    .filter((cd) => cd.visible)
    .map((cd) => cd.def);
    }

    get isExpandAll() {
    return this.expandedElements.length == this.dataSource.data.length;
    }

    toggleExpandAllElements() {
    if (!this.isExpandAll) {
    this.expandedElements = [...this.dataSource.data];
    } else {
    this.expandedElements = [];
    }
    }

    toggleExpandElement(element: PeriodicElement) {
    const index = this.expandedElements.indexOf(element);
    if (index > -1) {
    this.expandedElements.splice(index, 1);
    } else {
    this.expandedElements.push(element);
    }
    }

    isElementExpanded(element) {
    return this.expandedElements.indexOf(element) > -1;
    }
    }

    export interface PeriodicElement {
    Name: string;
    position: number;
    weight: number;
    symbol: string;
    description: string;
    }

    const ELEMENT_DATA: PeriodicElement[] = [
    {
    position: 1,
    Name: 'Hydrogen',
    weight: 1.0079,
    symbol: 'H',
    description: `Hydrogen is a chemical element with symbol H and atomic number 1. With a standard
    atomic weight of 1.008, hydrogen is the lightest element on the periodic table.`,
    },
    {
    position: 2,
    Name: 'Helium',
    weight: 4.0026,
    symbol: 'He',
    description: `Helium is a chemical element with symbol He and atomic number 2. It is a
    colorless, odorless, tasteless, non-toxic, inert, monatomic gas, the first in the noble gas
    group in the periodic table. Its boiling point is the lowest among all the elements.`,
    },
    ];


    The code above in the stackblitz link below along with other files like the css file. I would appreciate any assistance or guidance anyone has to get this to work.

    Demo - Stackblitz

    Continue reading...

Compartilhe esta Página