# Action Column Selects

Canonical page: https://www.adaptabletools.com/docs/handbook-action-column-select

- A `select` Control is a dropdown that writes a value back to the row
- It leverages the Select control provided by Adaptable Forms
- `options` can be a static list or a function of the row (e.g. distinct values from the Grid)

The Action Column `select` Control is designed to allow run-time users to choose one value from a list — status, license, owner etc. — without needing to open a Form.

It uses the Select Control that is provided by the [Adaptable Form](https://www.adaptabletools.com/docs/ui-tutorial-configuring-adaptable-forms/index.md).

## Options
The options in the Select Control are provided by the `options` property.

The object is of type [`AdaptableFormFieldOption`](https://www.adaptabletools.com/docs/reference/adaptableformfieldoption.md) (`label` / `value`) and can be 2 types:
- A **hardcoded** list can include values that are not currently in the Grid (e.g. `'Other'`)
- A **dynamic** list (often calls `getDistinctColumnValues` in [Grid API](https://www.adaptabletools.com/docs/handbook-managing-grid-data/index.md) to fetch existing values)

## Value & OnChange

The `value` and `onChange` properties work together:

- `value`is the current selection
- `onChange(value, context)` writes it back

You can use `placeholder` to set what is shown when nothing is selected

### Defining a Select Control

Put the Columns in `actionColumnOptions.actionColumns`.

Supply `columnId` and an optional `friendlyName` for each.

Set `type` to `'select'`.

An optional `placeholder` is shown when there is no value.

Each option is `{ label, value }` — same shape as Adaptable Form field.

The return values can be:

- hardcoded array - see **License** in 1st example (includes `'Other'` which doesn't appear in thedata)
- a function - see **Language** in 2nd example, which builds options from `gridApi.getDistinctColumnValues`

`value` reads the current selection from the row.

`onChange` receives the chosen `value` and the Action Column context, and writes it back.

```ts [[1, 1, "actionColumnOptions"], [1, 2, "actionColumns"], [2, 4, "columnId"], [2, 23, "columnId"], [2, 5, "friendlyName"], [2, 24, "friendlyName"], [3, 8, "type"], [3, 27, "type"], [4, 10, "options"], [4, 29, "options"], [5, 15, "value"], [5, 34, "value"], [6, 16, "onChange"], [6, 35, "onChange"]]
actionColumnOptions: {
  actionColumns: [
    {
      columnId: 'license_select',
      friendlyName: 'Set License',
      controls: [
        {
          type: 'select',
          placeholder: 'License',
          options: [
            {label: 'MIT License', value: 'MIT License'},
            {label: 'BSD 3-Clause', value: 'BSD 3-Clause'},
            {label: 'Other', value: 'Other'},
          ],
          value: (context) => context.data.license,
          onChange: (value, context) => {
            context.rowNode.setDataValue('license', value);
          },
        },
      ],
    },
    {
      columnId: 'language_select',
      friendlyName: 'Set Language',
      controls: [
        {
          type: 'select',
          placeholder: 'Language',
          options: (context) =>
            context.adaptableApi.gridApi
              .getDistinctColumnValues('language')
              .filter((v) => v != null && v !== '')
              .map((v) => ({ label: String(v), value: String(v) })),
          value: (context) => context.data.language,
          onChange: (value, context) => {
            context.rowNode.setDataValue('language', value);
          },
        },
      ],
    },
  ],
},
```

**Example: Action Columns: Select**

Select Controls that update a cell — static list and distinct Grid values

- `Set License` uses a **hardcoded** options list (including `Other`, which may not appear in the data yet)
- `Set Language` builds options **dynamically** from `gridApi.getDistinctColumnValues('language')`

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Action Column Select',
  actionColumnOptions: {
    actionColumns: [
      {
        columnId: 'license_select',
        friendlyName: 'Set License',
        settings: {width: 160, suppressMenu: true},
        controls: [
          {
            type: 'select',
            placeholder: 'License',
            value: context => context.data?.license ?? '',
            // Static list — can include values not currently in the Grid (e.g. Other)
            options: [
              {label: 'MIT License', value: 'MIT License'},
              {label: 'BSD 3-Clause', value: 'BSD 3-Clause'},
              {label: 'Other', value: 'Other'},
            ],
            onChange: (value, context) => {
              context.rowNode?.setDataValue('license', value);
            },
          },
        ],
      },
      {
        columnId: 'language_select',
        friendlyName: 'Set Language',
        settings: {width: 150, suppressMenu: true},
        controls: [
          {
            type: 'select',
            placeholder: 'Language',
            value: context => context.data?.language ?? '',
            // Function — options from distinct values currently in the language Column
            options: context =>
              context.adaptableApi.gridApi
                .getDistinctColumnValues('language')
                .filter(v => v != null && v !== '')
                .map(v => ({label: String(v), value: String(v)})),
            onChange: (value, context) => {
              context.rowNode?.setDataValue('language', value);
            },
          },
        ],
      },
    ],
  },
  initialState: {
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'language',
            'language_select',
            'license',
            'license_select',
            'github_stars',
          ],
          AutoSizeColumns: true,
        },
      ],
    },
  },
};
```
