DocsCore FeaturesRow Selection
Chapter 06

Row Selection

Single/multiple selection, checkboxes

Selection lets users pick one or many rows — for bulk actions, exporting just the selected rows, etc. Selection is off by default; you turn it on with the selection group.

What "selection" means here: the grid keeps a private list of which row IDs are currently ticked. It does not change your data — selecting a row just adds its ID to that list and highlights the row. You then read the list (via an event or the API) to do something with those rows: delete them, export them, show a "3 selected" toolbar, and so on.


The three selection modes

The mode option decides how many rows can be ticked at once:

Mode Behaviour Typical use
'none' Selection is off (the default). Read-only tables.
'single' At most one row selected; picking another clears the first — like radio buttons. "Choose one record to view."
'multiple' Any number of rows; checkboxes and a "select all" header. Bulk delete/export.

Turning selection on

const grid = createGrid<User>({
  columns,
  data,
  selection: {
    mode: 'multiple',   // 'none' | 'single' | 'multiple'
  },
})

Setting mode to anything other than 'none' automatically shows a checkbox column.


Selection options

Property Type Default Meaning
mode 'none' | 'single' | 'multiple' 'none' How many rows can be selected
selectAllScope 'page' | 'all' 'page' Does the header checkbox select the page or everything?
showCheckboxes boolean true (when mode ≠ none) Show the checkbox column
selectOnRowClick boolean true Clicking anywhere in a row selects it
checkboxOnly boolean false Only the checkbox selects (clicking the row does nothing)

Single selection (radio-style)

selection: { mode: 'single' }

Multiple selection with a "select all" that covers everything

The checkbox in the header is "select all". selectAllScope decides what "all" means when pagination or virtualization is in play:

  • 'page' (default) — ticks only the rows currently visible on this page. Safer: the user can clearly see everything they just selected.
  • 'all' — ticks every row that passes the current filter, even ones on other pages or scrolled out of view.
selection: {
  mode: 'multiple',
  selectAllScope: 'all',   // header checkbox selects ALL rows, not just this page
}

Checkbox-only selection

Useful when clicking a row should do something else (like open a detail panel) and only the checkbox should toggle selection:

selection: {
  mode: 'multiple',
  checkboxOnly: true,
  selectOnRowClick: false,
}

Reacting to selection changes

onSelectionChange receives the array of selected row objects (not just IDs), so you can act on the data directly:

events: {
  onSelectionChange: (selectedRows) => {
    console.log(`${selectedRows.length} rows selected`)
    console.log('Names:', selectedRows.map((r) => r.name))
  },
}

Controlling selection from code (the API)

api.selectRow('42')        // select by row ID
api.deselectRow('42')
api.toggleRow('42')        // flip its state
api.selectAll()
api.deselectAll()

api.isRowSelected('42')    // → boolean
api.getSelectedRows()      // → TData[] (the row objects)
api.getSelectedIds()       // → Set<string> (just the IDs)

getSelectedRows() vs getSelectedIds() — which do I want?

  • Use getSelectedRows() when you need the data — names to display, fields to send to a server.
  • Use getSelectedIds() when you only need identity — e.g. to pass to api.deleteRows([...]). It returns a Set, a JavaScript collection of unique values. A Set has no array methods like .map, so spread it into an array first when you need those: [...api.getSelectedIds()].

Remember: these use the row ID, which comes from your rowId setting (default field id). See Chapter 01.


Example — a bulk delete button

<script setup lang="ts">
import type { GridAPI } from '@elitegrid/vue'

const props = defineProps<{ api: GridAPI<User> }>()

function handleDelete() {
  const ids = [...props.api.getSelectedIds()]
  props.api.deleteRows(ids)        // bulk delete in one pass
  props.api.deselectAll()
}
</script>

<template>
  <button @click="handleDelete">Delete selected</button>
</template>

You'd grab api from the onReady event and store it in a ref — see Chapter 12.


Selection survives paging and filtering

Selection is tracked by row ID, independently of what's currently on screen — not by "which checkboxes are visible right now." That means:

  • Selecting rows on page 1, then moving to page 2, keeps page 1's rows selected.
  • Applying a filter that hides a selected row does not deselect it — it's just not visible while the filter is active. Clear the filter and it will still show as checked.

This is almost always what users expect ("I picked these 5 rows, don't lose them because I scrolled"), but it means a selection count in your UI should come from api.getSelectedIds().size (or the reactive useSelectionState composable — see below), not from counting checked boxes on the current page.

If you specifically want "select all" to mean only this page rather than every filtered row, that's what selectAllScope: 'page' (the default) already does — see above.

Reading selection reactively. Rather than manually tracking a count via onSelectionChange, Vue's useSelectionState(grid) composable gives you the selection state as a live, read-only Ref — it updates itself whenever the user (or your code) changes the selection:

import { useSelectionState } from '@elitegrid/vue'

const selection = useSelectionState(grid)
// selection.value.count, selection.value.selectedIds, selection.value.isAllSelected …

Pre-selecting rows on load

To open the grid with certain rows already selected (e.g. restoring a saved selection, or highlighting rows passed in as a prop), select them in onReady:

const grid = createGrid<User>({
  columns,
  data,
  selection: { mode: 'multiple' },
  events: {
    onReady: (api) => {
      const preselectedIds = ['12', '47', '88']
      preselectedIds.forEach((id) => api.selectRow(id))
    },
  },
})

Live example

Checkbox multi-select with a live "N selected" readout, using the useSelectionState composable introduced above — no manual onSelectionChange bookkeeping needed. Written as defineComponent + h(), per the note in Chapter 01.

import { defineComponent, h } from 'vue'
import { createGrid, Grid, useSelectionState, type GridAPI } from '@elitegrid/vue'

interface Employee {
  id: number
  name: string
  department: string
  role: string
}

const employees: Employee[] = [
  { id: 1, name: 'Ada Lovelace', department: 'Engineering', role: 'Staff Engineer' },
  { id: 2, name: 'Alan Turing', department: 'Research', role: 'Principal Scientist' },
  { id: 3, name: 'Grace Hopper', department: 'Engineering', role: 'Eng Manager' },
  { id: 4, name: 'Margaret Hamilton', department: 'Engineering', role: 'Tech Lead' },
  { id: 5, name: 'Katherine Johnson', department: 'Research', role: 'Senior Analyst' },
  { id: 6, name: 'Linus Torvalds', department: 'Engineering', role: 'Staff Engineer' },
  { id: 7, name: 'Tim Berners-Lee', department: 'Research', role: 'Principal Scientist' },
  { id: 8, name: 'Barbara Liskov', department: 'Engineering', role: 'Eng Manager' },
  { id: 9, name: 'Dennis Ritchie', department: 'Engineering', role: 'Staff Engineer' },
  { id: 10, name: 'Radia Perlman', department: 'Research', role: 'Senior Analyst' },
]

let api: GridAPI<Employee> | null = null

const grid = createGrid<Employee>({
  columns: [
    { field: 'name', header: 'Name', size: { flex: 2 } },
    { field: 'department', header: 'Department', size: { flex: 1.5 } },
    { field: 'role', header: 'Role', size: { flex: 1.5 } },
  ],
  data: employees,
  selection: { mode: 'multiple' },
  events: { onReady: (a) => { api = a } },
})

export default defineComponent({
  setup() {
    const selection = useSelectionState(grid)

    return () =>
      h('div', { style: 'height:440px;display:flex;flex-direction:column;gap:8px;padding:8px;box-sizing:border-box' }, [
        h('div', { style: 'display:flex;align-items:center;gap:12px;flex-shrink:0' }, [
          h('button', {
            onClick: () => api?.deselectAll(),
            style: 'padding:7px 16px;border-radius:7px;border:none;cursor:pointer;font-size:0.8rem;font-weight:600;font-family:system-ui;background:#7c3aed;color:#ffffff',
          }, 'Clear selection'),
          h('span', { style: 'font-size:0.8rem;color:#a1a1aa;font-family:system-ui' }, `${selection.value.count} selected`),
        ]),
        h('div', { style: 'flex:1;min-height:0' }, [h(Grid, { grid })]),
      ])
  },
})

Common selection mistakes

Symptom Cause Fix
"3 selected" badge shows the wrong count after filtering Counting checked DOM checkboxes instead of reading the API/composable Use api.getSelectedIds().size or useSelectionState(grid).value.count as the source of truth
selectRow('42') does nothing The ID passed doesn't match any row's rowId Double-check your rowId setting (see Chapter 01) — a mismatched type (42 vs '42') also silently fails to match
Clicking a row toggles selection when it shouldn't selectOnRowClick defaults to true Set selectOnRowClick: false (optionally with checkboxOnly: true) if rows should only select via their checkbox
"Select all" selected far more rows than expected selectAllScope: 'all' selects every filtered row, not just the visible page Use the default 'page' scope, or make the scope explicit in your UI copy ("Select all 240 results") so it isn't a surprise

Next: 07 · Inline Editing

Was this page helpful?