Row selection
Selection state and bulk workflows
When To Use Selection
Use row selection when users need to compare, batch, or act on rows. Do not use selection for ordinary row activation; row activation and selection can coexist but they represent different intents.
Selection State
Selection is stored by stable row id in the rowSelection state slice. Rows with a string or number id property use that value automatically. Provide getRowId when identity is composite or lives elsewhere.
readonly tableState = signal<Partial<NatTableUserState>>({
rowSelection: {
'position-1': true,
},
});
<nat-table-surface [(state)]="tableState">
<nat-table [data]="rows()" [columns]="columns" [enableRowSelection]="true" accessibleName="Selectable positions" />
</nat-table-surface>
Keep rowSelection in app state when bulk actions, URL persistence, or saved views need to survive table rerenders. Clear stale selected ids when the backing data set changes and selected rows no longer exist.
Selection Column
Use withNatTableSelectionColumn(...) for the standard leading checkbox column. Pair the helper with [enableRowSelection]="true" on <nat-table>.
import { withNatTableSelectionColumn } from 'ng-advanced-table/components';
readonly columns = withNatTableSelectionColumn(baseColumns, {
label: 'Select rows',
selectAllAriaLabel: 'Select all positions',
selectRowAriaLabel: (row) => `Select ${row.original.symbol}`,
});
The helper prepends a non-sortable, non-hideable, non-resizable column. It defaults to a 48px width, can be pinned, and uses generated locale labels unless you pass explicit overrides. Pin it with the normal columnPinning state when selection should stay visible during horizontal scrolling.
The select-all checkbox covers every row that matches the current filters, on every page — not just the page on screen. With 10 rows per page and 240 matching rows, checking it selects all 240, and it only shows as checked once all 240 are selected. Unchecking it clears those matching rows only: selected rows hidden by the current filters stay selected. With manual pagination the table only holds the rows you passed in, so select-all covers that loaded page. If users need a "select this page" control, build it in the app against the rowSelection state.
The generated column also sets meta.rowActivation: false, so a click that lands beside the checkbox never fires rowActivate; the checkbox is far smaller than the 24 px WCAG 2.5.8 target, and the cell padding around it must not act as a second, larger row target. Pass rowActivation: true to restore the old behavior. See Row Activation for the per-column flag.
Single And Multiple Selection
Multiple selection is the default. Use selectionMode="single" only when the workflow allows one selected row at a time. Clear or normalize app-owned selection when switching modes.
<nat-table
[data]="rows()"
[columns]="columns"
[enableRowSelection]="true"
selectionMode="single"
accessibleName="Selectable positions" />
In single-selection mode, the generated selection column renders the header as a plain column label instead of a select-all checkbox. Select-all is only meaningful for multiple selection.
Direct Selection Checkboxes
Most tables should use withNatTableSelectionColumn(...). Use NatTableSelectionCheckbox directly only when you are building a custom selection column or need to place the checkbox in a custom header or cell renderer.
import { flexRenderComponent } from 'ng-advanced-table';
import { NatTableSelectionCheckbox } from 'ng-advanced-table/components';
readonly columns = [
{
id: 'select',
header: (context) =>
flexRenderComponent(NatTableSelectionCheckbox, {
inputs: {
mode: 'all',
table: context.table,
label: 'Select rows',
},
}),
cell: (context) =>
flexRenderComponent(NatTableSelectionCheckbox, {
inputs: {
mode: 'row',
table: context.table,
row: context.row,
ariaLabel: `Select ${context.row.original.symbol}`,
},
}),
},
...baseColumns,
];
The checkbox component expects the table instance in both modes and the current row in row mode. Prefer locale providers for shared generated labels; pass ariaLabel only for table-specific copy.
Reading Selected Rows
rowSelection holds row ids, not rows. To get the selected row objects for a bulk action, map the ids back to your data with the same id rule the table uses: your getRowId, or the row's id property. With client-side pagination this works from your own data and state, so it covers selected rows on other pages and rows the current filters hide. With manual pagination, rows() holds only the loaded page, so selections made on other pages have no row object there: keep the ids and resolve them through your API (or a cache of rows you have loaded) before running the bulk action.
readonly rows = signal<Position[]>([]);
readonly tableState = signal<Partial<NatTableUserState>>({});
readonly getRowId = (row: Position): string => `position-${row.positionId}`;
readonly selectedRows = computed(() => {
const selection = this.tableState().rowSelection ?? {};
return this.rows().filter((row) => selection[this.getRowId(row)]);
});
<nat-table-surface [(state)]="tableState">
<nat-table
[data]="rows()"
[columns]="columns"
[getRowId]="getRowId"
[enableRowSelection]="true"
accessibleName="Selectable positions" />
</nat-table-surface>
Ids with no matching row, such as rows removed from the data set, drop out of selectedRows on their own. Clear them from rowSelection too, as described under Selection State.
Bulk Actions
Bulk controls belong to the consuming application. Place them in a toolbar when they are table commands, but keep domain side effects such as deleting rows, opening dialogs, or calling APIs outside the table.
Accessibility Notes
Rows expose aria-selected while selection is enabled. Multiple selection sets aria-multiselectable="true" on the grid, and selection changes are announced through the table live region.
Row selection and bulk actions
Selection state is keyed by stable row id and can drive app-owned bulk controls.
<nat-table-surface [enableSorting]="true" [enablePinning]="true" [state]="tableState()" (rowSelectionChange)="onRowSelectionChange($event)">
<nat-table-toolbar accessibleName="Bulk actions">
<button type="button" natToolbarItem [disabled]="selectedRows().length === 0">Delete selected</button>
</nat-table-toolbar>
<nat-table
[columns]="columns"
[data]="rows()"
[enableRowSelection]="true"
selectionMode="multiple"
accessibleName="Selectable rows" />
</nat-table-surface>readonly tableState = signal<Partial<NatTableUserState>>({ rowSelection: {} });
readonly columns = withNatTableSelectionColumn(
withNatTableHeaderActions(baseColumns),
{ label: 'Select service' }
);
onRowSelectionChange(rowSelection: RowSelectionState): void {
this.tableState.update((state) => ({ ...state, rowSelection }));
}