Row Expansion (Sub rows)

Row expansion enables tables to display hierarchical data by allowing rows to expand and reveal nested sub-rows. This is useful for representing parent-child relationships, grouped data, or tree structures within a table.

The demo below shows a table with expandable rows containing nested data. Each row with children displays an expand button that toggles visibility of its sub-rows. The header includes an expand-all button that expands or collapses all rows at once. Visual indentation indicates the depth level of each row in the hierarchy.

Username
Account Status Role Avg Session Duration Company Name Last Visited At Visit Count
import {Component} from "@angular/core"

import {ButtonModule} from "@qualcomm-ui/angular/button"
import {PaginationModule} from "@qualcomm-ui/angular/pagination"
import {ProgressRingModule} from "@qualcomm-ui/angular/progress-ring"
import {
  createAngularTable,
  createTablePagination,
  TableModule,
} from "@qualcomm-ui/angular/table"
import {
  getCoreRowModel,
  getExpandedRowModel,
  getPaginationRowModel,
} from "@qualcomm-ui/core/table"

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

@Component({
  imports: [TableModule, PaginationModule, ButtonModule, ProgressRingModule],
  selector: "row-expansion-demo",
  template: `
    <div q-table-root>
      <div q-table-action-bar>
        <button
          q-button
          size="sm"
          variant="outline"
          [disabled]="query.isFetching()"
          (click)="query.refetch()"
        >
          Refresh Data
        </button>
        @if (query.isFetching()) {
          <div q-progress-ring size="xs"></div>
        }
      </div>
      <div q-table-scroll-container>
        <table q-table-table>
          <thead q-table-header>
            @for (
              headerGroup of table.getHeaderGroups();
              track headerGroup.id
            ) {
              <tr q-table-row>
                @for (header of headerGroup.headers; track header.id) {
                  <th q-table-header-cell>
                    <ng-container *renderHeader="header; let value">
                      {{ value }}
                    </ng-container>
                  </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
        q-table-pagination
        [count]="pagination.count()"
        [page]="pagination.page()"
        [pageSize]="pagination.pageSize()"
        (pageChanged)="pagination.onPageChange($event)"
      >
        <div *paginationContext="let context" q-pagination-page-metadata>
          @let meta = context.pageMetadata;
          {{ meta.pageStart }}-{{ meta.pageEnd }} of {{ meta.count }} results
        </div>

        <div q-pagination-page-buttons></div>
      </div>
    </div>
  `,
})
export class RowExpansionDemo {
  protected readonly query = createUserQuery(100, 5, 3)

  protected table = createAngularTable<User>(() => ({
    columns: userColumns,
    data: this.query.data() || [],
    getCoreRowModel: getCoreRowModel(),
    getExpandedRowModel: getExpandedRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSubRows: (row: User) => row.subRows,
  }))

  protected pagination = createTablePagination(this.table)
}
Last updated on by Ryan Bower