# Row Selection

**index**

* [Single Row Selection](#single-row-selection)
* [Multiple Row Selections](#multiple-row-selections)
* [Change Dynamically Single/Multiple Selections](#changing-dynamically-from-single-to-multiple-selections-and-vice-versa)
* [Mixing Single & Multiple Row Selections](#mixing-single--multiple-row-selections)
* [Disable Custom Rows Selections via `selectableOverride`](#disable-custom-rows-selections-via-selectableoverride)
* [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection)
* [Change Row Selections](#change-row-selections)
* [Understanding the multiSelect Option](#understanding-the-multiselect-option)
* Troubleshooting
  * [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that)
* [Hybrid Selection Model (cell+row selection)](#hybrid-selection-model-and-drag-fill)

#### Description

For row selection, you can simply play with couple of grid options (see below) and subscribe to `onSelectedRowsChanged` (a SlickGrid Event that is, it's not an Observable). However please note that `onSelectedRowsChanged` is a function available on the `Grid` object and you will need bind to `(gridChanged)` to get the object when grid is ready. There are 2 types of row selection(s) which you can do.

**Note:** `enableCheckboxSelector` and `enableExcelCopyBuffer` do not work well together, this is because they both share the same `Row.SelectionModel` and one cancels the other. It is recommended to not use `enableExcelCopyBuffer` in that case.

#### Demo

[Demo Page](https://ghiscoding.github.io/slickgrid-universal/#/example07) / [Demo ViewModel](https://github.com/ghiscoding/slickgrid-universal/blob/master/demos/vanilla/src/examples/example07.ts)

### Single Row Selection

For a single row selection, you need to have `enableCellNavigation: true`, `enableRowSelection: true` and `multiSelect: false` and as described earlier, subscribe to `onSelectedRowsChanged` (for that you need to bind to `(gridChanged)`). There are 2 ways to choose for the implementation of a row selection, option **1.** is the most common option and is the recommend way of doing it.

#### 1. with Event Listener (preferred way)

You can also do it through a Custom Event listener since all SlickGrid events are exposed as Custom Event. For more info see [Wiki - OnEvents](https://github.com/ghiscoding/slickgrid-universal/blob/master/docs/grid-functionalities/grid-dataview-events.md)

**ViewModel**

```ts
export class Example1 {
  attached() {
    this.initializeGrid();
    this.dataset = this.loadData(500);
    const gridContainerElm = document.querySelector<HTMLDivElement>(`.grid3`);

    gridContainerElm.addEventListener('onselectedrows', this.handleOnClick.bind(this));
    this.sgb = new Slicker.GridBundle(gridContainerElm, this.columns, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
  }

  initializeGrid() {
    // define columns
    ...

    // grid options
    this.gridOptions = {
      enableAutoResize: true,
      enableCellNavigation: true,
      enableCheckboxSelector: true,
      enableRowSelection: true,
      multiSelect: false,
    }
  }

  handleRowSelection(event) {
    const args = event?.detail?.args;
    console.log(event, args);
  }
}
```

#### 2. with SlickGrid object & onEvent

It's preferable to use the Custom Events, but if you really wish, you can also use directly the SlickGrid event that you can subscribe to. However, don't forget to unsubscribe to a SlickGrid event.

**ViewModel**

```ts
this.gridOptions = {
  enableAutoResize: true,
  enableCellNavigation: true,
  enableRowSelection: true
}

gridObjChanged(grid) {
  grid.onSelectedRowsChanged.subscribe((e, args) => {
    if (Array.isArray(args.rows)) {
      this.selectedObjects = args.rows.map(idx => {
        const item = grid.getDataItem(idx);
        return item.title || '';
      });
    }
  });
}
```

### Multiple Row Selections

As for multiple row selections, you need to enable `enableCheckboxSelector` and `enableRowSelection`, keep `multiSelect` enabled (default is `true`), and typically use `selectionOptions.selectActiveRow: false` when you do not want active-row clicks to interfere with checkbox-based multi-selection. Then as describe earlier, you will subscribe to `onSelectedRowsChanged` (for that you need to bind to `(gridChanged)`). There are 2 ways to choose for the implementation of a row selection, option **1.** is the most common option and is the recommend way of doing it.

#### 1. with Custom Events (preferred way)

You can also do it through a Custom Event listener since all SlickGrid events are exposed as Custom Events. For more info see [Wiki - OnEvents](https://github.com/ghiscoding/slickgrid-universal/blob/master/docs/grid-functionalities/grid-dataview-events.md)

**ViewModel**

```ts
export class Example1 {
  attached() {
    this.initializeGrid();
    this.dataset = this.loadData(500);
    const gridContainerElm = document.querySelector<HTMLDivElement>(`.grid3`);

    gridContainerElm.addEventListener('onselectedrows', this.handleOnClick.bind(this));
    this.sgb = new Slicker.GridBundle(gridContainerElm, this.columns, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
  }

  initializeGrid() {
    // define columns
    ...

    // grid options
    this.gridOptions = {
      enableAutoResize: true,
      enableCellNavigation: true,
      enableCheckboxSelector: true,
      enableRowSelection: true,
      // `rowSelectionOptions` in <=9.x OR `selectionOptions` in >=10.x
      selectionOptions: {
        // True (Single Selection), False (Multiple Selections)
        selectActiveRow: false
      },
      // keep `multiSelect` enabled (default) for actual multiple row selection
    }
  }

  handleRowSelection(event) {
    const args = event?.detail?.args;
    console.log(event, args);
  }
}
```

#### 2. with SlickGrid object & onEvent

It's preferable to use the Custom Event listeners, but if you really wish, you can also use directly the SlickGrid event that you can subscribe to. However, don't forget to unsubscribe to a SlickGrid event.

**ViewModel**

```ts
export class Example1 {
  defineGrid() {
    this.gridOptions = {
      enableAutoResize: true,
      enableCellNavigation: true,
      enableCheckboxSelector: true,
      enableRowSelection: true,
      // `rowSelectionOptions` in <=9.x OR `selectionOptions` in >=10.x
      selectionOptions: {
        // True (Single Selection), False (Multiple Selections)
        selectActiveRow: false
      },
      // keep `multiSelect` enabled (default) for actual multiple row selection
    }
  }

  gridObjChanged(grid) {
    grid.onSelectedRowsChanged.subscribe((e, args) => {
      if (Array.isArray(args.rows)) {
        this.selectedObjects = args.rows.map(idx => {
          const item = grid.getDataItem(idx);
          return item.title || '';
        });
      }
    });
  }
}
```

### Changing Dynamically from Single to Multiple Selections (and vice-versa)

If you want to change from Multiple Selections to Single Selection (and vice-versa), you could toggle the grid options `enableCellNavigation` flag (`False` when you want Single Selection), however this is not possible when using Inline Editors since this flag is required. Note that there is currently no other ways of toggling dynamically without re-creating the grid.

### Mixing Single & Multiple Row Selections

SlickGrid is so powerful and customizable, you could if you wish mix the multiple row selections (cell column 1) and single row selection (any other cell click). For that though, you will need to use 2 SlickGrid Events (`onClick` and `onSelectedRowsChanged`). For example with a Custom Event listener we can do it this way:

**ViewModel**

```ts
export class Example1 {
  handleMultipleRowSelections(event) {
    const args = event?.detail?.args;
    console.log('multiple row checkbox selected', event, args);
  }

  handleSingleRowClick(event) {
    const args = event?.detail?.args;
    console.log('multiple row checkbox selected', event, args);

    // when clicking on any cell, we will make it the new selected row
    // however, we don't want to interfere with multiple row selection checkbox which is on 1st column cell
    if (args.cell !== 0) {
      grid.setSelectedRows([args.row]);
    }
  }
}
```

### Disable Custom Rows Selections via `selectableOverride`

You can use `selectableOverride` to provide custom logic to disable certain rows selections, for example the code below will remove the row selection on every second row.

**Component**

```typescript
export class Example1 implements OnInit {
  prepareGrid() {
    this.gridOptions = {
      enableRowSelection: true,
      enableCheckboxSelector: true,
      checkboxSelector: {
        // you can override the logic for showing (or not) the expand icon
        // for example, display the expand icon only on every 2nd row
        selectableOverride: (row: number, dataContext: any, grid: any) => (dataContext.id % 2 === 1)
      },
      multiSelect: false,
      // `rowSelectionOptions` in <=9.x OR `selectionOptions` in >=10.x
      selectionOptions: {
        // True (Single Selection), False (Multiple Selections)
        selectActiveRow: true,
      },
    };
  }
}
```

#### Disable External Button when having Empty Selection

When having an external button that you want to work only when there's row selection, there are 2 ways of doing this.

1. use the `onSelectedRowsChanged` event (via your View in HTML or via ViewModel)

```html
<button disabled.bind="isMyButtonDisabled">My Button</button>
<div class="myGrid"
    onselectedrowschanged.trigger="handleOnSelectedRowsChanged">
</div>
```

```ts
isMyButtonDisabled = false;

handleOnSelectedRowsChanged(event) {
  const args = event?.detail?.args;
  this.isMyButtonDisabled = args.rows?.length === 0;
}
```

2. use the `onGridStateChanged` event (see [Grid State & Presets](/slickgrid-universal/grid-functionalities/grid-state-preset.md) Wiki)

```html
<button disabled.bind="isMyButtonDisabled">My Button</button>
<div class="myGrid"
    ongridstatechanged.trigger="handleOngridStateChanged">
</div>
```

```ts
isMyButtonDisabled = false;

handleOngridStateChanged(event) {
  const gridState = event && event.detail && event.detail.gridState;
  if (Array.isArray(gridState?.rowSelection.dataContextIds)) {
    this.isMassSelectionDisabled = gridState.rowSelection.dataContextIds.length === 0;
  }
}
```

#### Change Row Selections

You can change which row(s) are selected by using the built-in SlickGrid method `setSelectedRows(rowIndexes)` (passing an empty array will clear all selection), however please note that it requires an array of row indexes as you see them in the UI and it won't work that great with Pagination (if that is what you are looking for then take a look at this Stack Overflow [Q\&A](https://stackoverflow.com/questions/59629565/want-to-change-gridoption-preselectedrows-row-in-angular-slickgrid-dynamically-o))

```ts
export class Example1 {
  attached() {
    this.initializeGrid();
    this.dataset = this.loadData(500);
    const gridContainerElm = document.querySelector<HTMLDivElement>(`.grid3`);
    this.sgb = new Slicker.GridBundle(gridContainerElm, this.columns, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
  }

  clearRowSelection() {
    this.sgb.slickGrid.setSelectedRows([]); // empty array will clear the row selection
  }
  changeRowSelections() {
    this.sgb.slickGrid.setSelectedRows(rowIndexes);

    // OR providing an empty array will clear the row selection
    // this.sgb.slickGrid.setSelectedRows([]);
  }
}
```

#### Hybrid Selection Model

Starting with v9.10.0, you can now use the new Hybrid Selection Model, this new model will allow you to do Cell Selection & Row Selection in the same grid. This wasn't previously doable before that version because SlickGrid only ever allows 1 selection model to be loaded at once and so we had to load either `SlickCellSelectionModel` or `SlickRowSelectionModel` but never both of them at the same time. The new Hybrid Selection Model is merging both of these plugins in a single plugin allowing us to do both type of selections.

> \[!NOTE] You can use `enableHybridSelection: true` grid option to enable the new Hybrid Model, this new model will eventually replace both cell/row selection model in the future since there's no need to keep all these models when only 1 is more than enough

For example, we could use the Excel Copy Buffer (Cell Selection) and use `rowSelectColumnIds` (Row Selection)

```ts
this.gridOptions = {
  // enable new hybrid selection model (rows & cells)
  enableHybridSelection: true,
  // `rowSelectionOptions` in <=9.x OR `selectionOptions` in >=10.x
  selectionOptions: {
    selectActiveRow: true,
    rowSelectColumnIds: ['selector'],
  },

  // when using the ExcelCopyBuffer, you can see what the selection range is
  enableExcelCopyBuffer: true,
  excelCopyBufferOptions: {
    copyActiveEditorCell: true,
    removeDoubleQuotesOnPaste: true,
    replaceNewlinesWith: ' ',
  },
};
```

**Hybrid Selection Model and Drag-Fill**

You can also `onDragReplaceCells` event to drag and fill cell values to the extended cell selection.

**ViewModel**

```ts
export class Example1 {
  attached() {
    this.initializeGrid();
    this.dataset = this.loadData(500);
    const gridContainerElm = document.querySelector<HTMLDivElement>(`.grid3`);

    gridContainerElm.addEventListener('ondragreplacecells', this.handleOnClick.bind(this));
    this.sgb = new Slicker.GridBundle(gridContainerElm, this.columns, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
  }

  initializeGrid() {
    this.gridOptions = {
      // enable new hybrid selection model (rows & cells)
      enableHybridSelection: true,
      // ...
    };
  }

  /** Copy the dragged cell values to other cells that are part of the extended drag-fill selection */
  copyDraggedCellRange(args: OnDragReplaceCellsEventArgs) {
    const verticalTargetRange = SlickSelectionUtils.verticalTargetRange(args.prevSelectedRange, args.selectedRange);
    const horizontalTargetRange = SlickSelectionUtils.horizontalTargetRange(args.prevSelectedRange, args.selectedRange);
    const cornerTargetRange = SlickSelectionUtils.cornerTargetRange(args.prevSelectedRange, args.selectedRange);

    if (verticalTargetRange) {
      SlickSelectionUtils.copyCellsToTargetRange(args.prevSelectedRange, verticalTargetRange, args.grid);
    }
    if (horizontalTargetRange) {
      SlickSelectionUtils.copyCellsToTargetRange(args.prevSelectedRange, horizontalTargetRange, args.grid);
    }
    if (cornerTargetRange) {
      SlickSelectionUtils.copyCellsToTargetRange(args.prevSelectedRange, cornerTargetRange, args.grid);
    }
  }
}
```

### Understanding the `multiSelect` Option

The `multiSelect` grid option is a critical setting that controls how row selection works across all selection methods (checkboxes, keyboard navigation, and click handlers). Understanding this option is key to implementing the correct selection behavior for your use case.

#### `multiSelect: false` (Single Selection Mode)

When `multiSelect: false`, the grid enforces **strict single selection**:

* **Checkbox Selection**: Clicking a checkbox selects only that row. Clicking a checkbox that's already selected will **deselect** it (toggle behavior). Only one row can be selected at a time.
* **Keyboard Selection**: Using Shift+Arrow keys to extend a range will select only the current row (range is clamped to a single row). Regular arrow navigation moves between rows but doesn't change selection.
* **Overall Behavior**: At most one row is selected at any time. If you programmatically select a row while another is selected, only the new row remains selected.

#### `multiSelect: true` (Multiple Selection Mode)

When `multiSelect: true`, the grid allows **multiple rows to be selected**:

* **Checkbox Selection**: Clicking a checkbox adds or removes that row from the selection set. Multiple rows can be checked independently.
* **Keyboard Selection**: Using Shift+Arrow keys extends the selection range to include multiple rows. Ctrl/Cmd+Click or Shift+Click can be used to build complex selections.
* **Overall Behavior**: Multiple rows can be selected and retained in the selection set.

#### Selection Methods & `multiSelect`

All row selection methods respect the `multiSelect` option:

1. **Checkbox Selection** (when `enableCheckboxSelector: true`): Respects `multiSelect` setting
2. **Keyboard Selection** (Shift+Arrow): Respects `multiSelect` setting
3. **Programmatic Selection** (calling `setSelectedRows()`): Not restricted by `multiSelect` but subsequent UI interactions will respect it
4. **Other Selection Handlers**: All adhere to the `multiSelect` constraint

`selectionOptions.selectActiveRow` only controls whether activating a row also selects it; it does not override `multiSelect` and cannot enable multiple selection when `multiSelect: false`.

### Troubleshooting

#### Adding a Column dynamically is removing the Row Selection column, why is that?

The reason is because the Row Selection (checkbox) plugin is a special column and Slickgrid-Universal is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columns`. To address this issue, you need to get the Slickgrid-Universal internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work.

```ts
const newColumn = { /*...*/ };

const allColumns = this.sgb.gridService.getAllColumnDefinitions();
allColumns.push(newColumn);
this.columns = allColumns.slice(); // or use spread operator [...cols]

// you could also use SlickGrid setColumns() method
// this.sgb.slickGrid.setColumns(cols);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ghiscoding.gitbook.io/slickgrid-universal/grid-functionalities/row-selection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
