Angular Data Grid Column Ordering & Visibility

Column ordering & visibility

Let users reorder Angular Data Grid columns by drag-and-drop and hide or show columns through a column chooser.

Give users control over the layout: drag headers to reorder columns and toggle columns on and off to focus on what matters. Both features are driven through the headless table, so you can wire them into your own toolbar — as the column chooser in this demo does.

Column ordering & visibility

columnOrder makes headers draggable — drop one onto another to reorder (dragging never crosses a pinning boundary); pass an array for an initial order. columnVisibility enables hiding and showing columns through the headless table (column.toggleVisibility(), column.getIsVisible()); pass an object like { email: false } to start with a column hidden. Opt a column out with movable: false / hideable: false. This demo adds a column chooser built entirely with the toolbar slot template.

1 Alice Smith [email protected] admin
2 Bob Jones [email protected] editor
3 Carol Brown [email protected] viewer
4 Dave Taylor [email protected] admin
5 Eve Wilson [email protected] editor
6 Frank Davies [email protected] viewer
7 Grace Evans [email protected] admin
8 Heidi Thomas [email protected] editor
9 Ivan Smith [email protected] viewer
10 Judy Jones [email protected] admin
1–10 of 1000
ts
import { Component } from '@angular/core'
import { DataGridComponent, DataGridSlotDirective } from '@coreui/angular-data-grid'
import type { Column, DataGridColumn, DataGridItem } from '@coreui/angular-data-grid'

const firstNames = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Heidi', 'Ivan', 'Judy']
const lastNames = ['Smith', 'Jones', 'Brown', 'Taylor', 'Wilson', 'Davies', 'Evans', 'Thomas']
const roles = ['admin', 'editor', 'viewer']

@Component({
  selector: 'docs-data-grid-order-visibility-example',
  imports: [DataGridComponent, DataGridSlotDirective],
  template: `
    <c-data-grid
      [columns]="columns"
      [items]="items"
      [itemKey]="itemKey"
      [columnOrder]="true"
      [columnVisibility]="true"
      [pagination]="{ pageSize: 10 }"
    >
      <!-- Column chooser built with the toolbar slot and the headless table -->
      <ng-template cDataGridSlot="toolbar" let-table>
        <div class="d-flex gap-3 mb-2">
          @for (column of table.getAllLeafColumns(); track column.id) {
            @if (column.getCanHide()) {
              <label class="form-check form-check-inline m-0">
                <input
                  type="checkbox"
                  class="form-check-input me-1"
                  [checked]="column.getIsVisible()"
                  (change)="toggleColumn(column, $event)"
                />
                {{ column.id }}
              </label>
            }
          }
        </div>
      </ng-template>
    </c-data-grid>
  `
})
export class DataGridOrderVisibilityExample {
  readonly columns: DataGridColumn[] = [
    // movable/hideable: false keeps the key column in place
    {
 key: 'id', label: '#', width: 90, movable: false, hideable: false
},
    { key: 'name', label: 'Name' },
    { key: 'email', label: 'Email', style: { width: '30%' } },
    { key: 'role', label: 'Role', width: 110 }
  ]
readonly items: DataGridItem[] = Array.from({ length: 1000 }, (_, i) => {
    const name = `${firstNames[i % firstNames.length]} ${lastNames[i % lastNames.length]}`
    return {
      id: i + 1,
      name,
      email: `${name.toLowerCase().replace(' ', '.')}${i}@example.com`,
      role: roles[i % roles.length]
    }
  })
readonly itemKey = (item: DataGridItem) => String(item.id)

  toggleColumn(column: Column<DataGridItem, unknown>, event: Event) {
    column.toggleVisibility((event.target as HTMLInputElement).checked)
  }
}

The drag interaction

Dragging is pointer-based, not HTML5 drag-and-drop, which is what makes the following possible at all — and makes it behave the same with a mouse, a pen and a finger:

  • A ghost follows the pointer with the column’s label, and the source header dims while it travels.
  • A live preview — the columns the dragged one passes slide aside, and it slides into the gap, so the grid shows the layout the drop will produce instead of pointing at it with a line. A swap fires the moment the pointer crosses into a neighbour — at its edge, not its midpoint — and the release commits whatever the preview shows. Nothing reflows: it is transform only, and the real order still changes once, on drop. Where a drop is not allowed (another pinned region), nothing slides — and a release past either end of the axis lands the column at that end.
  • Auto-scroll at the viewport’s edges, ramping with how far into the edge the pointer sits, so a column can cross a grid wider than the screen.
  • The ghost settles — on release it flies from wherever you let go to the slot the column landed in and fades out there; on Escape it flies back home.
  • Escape cancels mid-drag, and the click that follows a drag never toggles the sort of the header it started from.

A press only becomes a drag after it travels a few pixels, so clicking a sortable header still sorts it.

The slide honours prefers-reduced-motion: someone who asked the OS to stop moving things still sees the preview, snapped to the final layout instead of gliding there. Retime it with the --cui-data-grid-drag-transition token.

Options

OptionTypeDefaultDescription
columnOrderboolean | string[]falseDrag-and-drop reordering; an array sets the initial order. Never crosses a pinning boundary.
columnVisibilityboolean | Record<string, boolean>falseEnables hiding/showing columns; an object sets the initial visibility.
movable (column)booleantrueSet false to exclude a column from reordering.
hideable (column)booleantrueSet false to prevent hiding a column.

Reordering emits the orderChange output { columnOrder }; toggling visibility emits the visibilityChange output { columnVisibility }. The column menu offers a keyboard-accessible Move to start/end as an alternative to drag-and-drop.