Column Ordering (Drag & Drop)

This example features drag and drop column reordering using the @angular/cdk drag and drop directives.

Username Account Status Role Avg Session Duration Company Name Last Visited At Visit Count
Hayley_Abbott29 active admin 85 Hyatt LLC 27 Sep 2024 08:27:39 PDT 250
Aimee_Effertz57 active admin 117 McKenzie - Price 22 Feb 2025 13:23:52 PDT 6
Erick_Torphy55 suspended moderator 12 Pollich, Zieme and Dach 20 Sep 2025 17:31:23 PDT 344
Viva_Doyle active admin 117 Stamm, Fisher and Kunde 24 Jun 2025 01:18:49 PDT 10
Teagan_Hudson suspended moderator 119 Huels, Windler and Daniel 16 Jun 2025 03:16:54 PDT 48
Wayne_Simonis active user 117 Stiedemann, Roob and Price 16 Jul 2025 08:08:23 PDT 9
Carolina_Hegmann25 active moderator 119 Gerhold, Brown and Dickinson 02 Apr 2025 01:18:09 PDT 5
Ewell.King70 active user 74 Littel, Wolf and Nienow 28 Oct 2025 14:48:48 PDT 2
Dallas_Bode suspended admin 119 Runolfsson, Heaney and Stamm 23 Nov 2024 22:16:14 PDT 2
Gregory_Daniel active user 99 Hagenes, Wunsch and Gleichner 11 Jul 2025 18:38:56 PDT 180
import {
  CdkDrag,
  type CdkDragDrop,
  CdkDragHandle,
  CdkDropList,
} from "@angular/cdk/drag-drop"
import {Component, signal} from "@angular/core"

import {
  type AngularTable,
  createAngularTable,
  TableModule,
} from "@qualcomm-ui/angular/table"
import {type ColumnOrderState, getCoreRowModel} from "@qualcomm-ui/core/table"

import {createUserQuery, type User, userColumns} from "./data"

@Component({
  imports: [TableModule, CdkDropList, CdkDrag, CdkDragHandle],
  selector: "column-dnd-demo",
  template: `
    <div q-table-root>
      <div q-table-scroll-container>
        <table q-table-table>
          <thead q-table-header>
            @for (
              headerGroup of table.getHeaderGroups();
              track headerGroup.id
            ) {
              <tr
                cdkDropList
                cdkDropListOrientation="horizontal"
                q-table-row
                (cdkDropListDropped)="onColumnDropped($event)"
              >
                @for (header of headerGroup.headers; track header.id) {
                  @if (!header.isPlaceholder) {
                    <th cdkDrag q-table-header-cell>
                      <ng-container *renderHeader="header; let value">
                        {{ value }}
                      </ng-container>
                      <button cdkDragHandle q-table-column-drag-handle></button>
                    </th>
                  }
                }
              </tr>
            }
          </thead>
          <tbody q-table-body>
            @for (row of table.getRowModel().rows; track row.id) {
              <tr q-table-row>
                @for (cell of row.getVisibleCells(); track cell.id) {
                  <td q-table-cell>
                    <ng-container *renderCell="cell; let value">
                      {{ value }}
                    </ng-container>
                  </td>
                }
              </tr>
            }
          </tbody>
        </table>
      </div>
    </div>
  `,
})
export class ColumnDndDemo {
  readonly query = createUserQuery(10)

  onColumnDropped(col: CdkDragDrop<any>) {
    this.columnOrder.update((prevOrder) => {
      const columnOrder = [...prevOrder]
      return this.reorderColumn(
        columnOrder[col.previousIndex],
        columnOrder[col.currentIndex],
        columnOrder,
      )
    })
  }

  private reorderColumn(
    draggedColumnId: string,
    targetColumnId: string,
    columnOrder: string[],
  ): ColumnOrderState {
    columnOrder.splice(
      columnOrder.indexOf(targetColumnId),
      0,
      columnOrder.splice(columnOrder.indexOf(draggedColumnId), 1)[0],
    )
    console.debug("new order", columnOrder)
    return columnOrder
  }

  protected readonly columnOrder = signal(
    userColumns.map((column) => column.id!),
  )

  protected table: AngularTable<User> = createAngularTable(() => ({
    columns: userColumns,
    data: this.query.data() || [],
    getCoreRowModel: getCoreRowModel(),
    state: {
      columnOrder: this.columnOrder(),
    },
  }))
}
Last updated on by Ryan Bower