---
title: "Rich Select Cell Editor - Async Values"
enterprise: true
framework: vue
version: "36.1.0"
---

# Rich Select Cell Editor - Async Values

The Rich Select Cell Editor supports loading values asynchronously, including paged loading and server-side filtering.

## Async Values

List values can be provided asynchronously to the editor as shown below:

When `values` is provided as a callback function, the callback receives `params` with type `RichCellEditorValuesCallbackParams`.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `values` | [`TValue[] \| RichCellEditorValuesCallback`](https://www.ag-grid.com/vue-data-grid/typescript-generics/#cell-value-tvalue) |  |  | The list of values to be selected from. Required when `valuesPage` is not provided. |

#### Rich Select Async Values

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IRichCellEditorParams,
  ModuleRegistry,
  RichCellEditorValuesCallbackParams,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { RichSelectModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextEditorModule,
  ClientSideRowModelModule,
  RichSelectModule,
]);

const languages = ["English", "Spanish", "French", "Portuguese", "(other)"];

function getRandomNumber(min: number, max: number) {
  // min and max included
  return Math.floor(window.agRandom() * (max - min + 1) + min);
}

function getValueFromServer(
  _params: RichCellEditorValuesCallbackParams,
): Promise<string[]> {
  // simulates an async request to a server
  return new Promise((resolve) => {
    setTimeout(() => resolve(languages), 1000);
  });
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<ColDef[]>([
      {
        field: "language",
        cellEditor: "agRichSelectCellEditor",
        cellEditorParams: {
          values: getValueFromServer,
        } as IRichCellEditorParams,
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 200,
      editable: true,
    });
    const rowData = ref<any[] | null>(
      new Array(100)
        .fill(null)
        .map(() => ({ language: languages[getRandomNumber(0, 4)] })),
    );

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Rich Select Async Values](https://www.ag-grid.com/examples/provided-cell-editors-rich-select-async/rich-select-async-values/vue3)

```js
columnDefs: [
    {
        field: 'language',
        cellEditor: 'agRichSelectCellEditor',
        cellEditorParams: {
            values: (_params) => fetch('/api/languages').then((res) => res.json()),
        }
    }
]
```

## Paged Async Values

For large datasets, `valuesPage` avoids loading all items at once and instead loads values on demand based on user scroll and interactions. `valuesPageInitialStartRow` can be used to set the initial position. The `valuesPage` callback receives `params` with type `RichCellEditorValuesPageParams` and should return a value that conforms to the `RichCellEditorValuesPageResult` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `valuesPage` | `RichCellEditorValuesPageCallback` |  |  | Optional paged datasource for very large value lists. When provided, values are loaded incrementally and additional pages are requested as the user scrolls. If both `values` and `valuesPage` are set, `valuesPage` takes precedence. |
| `valuesPageInitialStartRow` | `number \| RichCellEditorValuesPageStartRowCallback` |  | `0` | Initial page start row when using `valuesPage`. Can be a fixed number or a callback that derives the start row from the current editor value. Only applied for the initial, unfiltered load. Filtered searches always start from row `0`. |
| `valuesPageSize` | `number` |  | `100` | Number of rows requested per page when using `valuesPage`. |
| `valuesPageLoadThreshold` | `number` |  | `10` | Number of rows from the end of the loaded list at which the next page is requested. |

#### Rich Select Paged Async Values

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IRichCellEditorParams,
  ModuleRegistry,
  RichCellEditorValuesPageParams,
  RichCellEditorValuesPageResult,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { RichSelectModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextEditorModule,
  ClientSideRowModelModule,
  RichSelectModule,
]);

const languages = new Array(20000)
  .fill(null)
  .map((_, index) => `Language ${index + 1}`);

function getRandomNumber(min: number, max: number) {
  // min and max included
  return Math.floor(window.agRandom() * (max - min + 1) + min);
}

function getValuePageFromServer(
  params: RichCellEditorValuesPageParams,
): Promise<RichCellEditorValuesPageResult<string>> {
  // Simulates an async request to a server
  return new Promise((resolve) => {
    setTimeout(() => {
      const pageValues = languages.slice(params.startRow, params.endRow);
      const nextOffset =
        params.endRow < languages.length ? String(params.endRow) : null;
      resolve({
        values: pageValues,
        lastRow: languages.length,
        cursor: nextOffset,
      });
    }, 300);
  });
}

function getInitialStartRowForValue(value: string | null | undefined): number {
  if (!value) {
    return 0;
  }
  const match = /^Language (\d+)$/.exec(value);
  if (!match) {
    return 0;
  }
  const selectedIndex = Number(match[1]) - 1;
  return Math.max(selectedIndex - 50, 0);
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<ColDef[]>([
      {
        field: "language",
        width: 300,
        editable: true,
        cellEditor: "agRichSelectCellEditor",
        cellEditorParams: {
          valuesPage: getValuePageFromServer,
          valuesPageInitialStartRow: (value: string | null | undefined) =>
            getInitialStartRowForValue(value),
          valuesPageSize: 100,
          valuesPageLoadThreshold: 8,
        } as IRichCellEditorParams,
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 220,
      editable: true,
    });
    const rowData = ref<any[] | null>(
      new Array(100)
        .fill(null)
        .map(() => ({
          language: languages[getRandomNumber(0, languages.length - 1)],
        })),
    );

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Rich Select Paged Async Values](https://www.ag-grid.com/examples/provided-cell-editors-rich-select-async/rich-select-paged-async-values/vue3)

```js
columnDefs: [
    {
        cellEditor: 'agRichSelectCellEditor',
        cellEditorParams: {
            valuesPage: (params) => {
                return fetch(`/api/languages?
                    startRow=${params.startRow}
                    &endRow=${params.endRow}`)
                .then((res) => res.json());
            },
            valuesPageInitialStartRow: (value) => getRowForSelectedValue(value),
            valuesPageSize: 100,
            valuesPageLoadThreshold: 10
        }
    }
]
```

## Async Filtering

For advanced filtering scenarios, combine async `values` callback, `allowTyping`, `filterList: true`, and `filterListAsync` to enable async filtering. The `values` callback params remain typed as `RichCellEditorValuesCallbackParams`.

When `filterListAsync` is set to `true`, the cell editor behaves as follows:

- It calls the `values` callback with the current search term.
- It delays the search request by 300ms (this can be adjusted using `searchDebounceDelay`) to avoid excessive calls.
- A loading indicator appears while the network request is in progress.
- Once the promise resolves, the dropdown is updated with the filtered results.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `values` | [`TValue[] \| RichCellEditorValuesCallback`](https://www.ag-grid.com/vue-data-grid/typescript-generics/#cell-value-tvalue) |  |  | The list of values to be selected from. Required when `valuesPage` is not provided. |
| `filterListAsync` | `boolean` |  | `false` | Set to `true` to enable asynchronous filtering of values via the `values` or `valuesPage` callback. (only relevant when `allowTyping=true` and `filterList=true`). |
| `searchDebounceDelay` | `number` |  | `300` | The value in `ms` for the search algorithm debounce delay |

This is shown in the example below:

#### Rich Select Async Filtering

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IRichCellEditorParams,
  ModuleRegistry,
  RichCellEditorValuesCallbackParams,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { RichSelectModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextEditorModule,
  ClientSideRowModelModule,
  RichSelectModule,
]);

const languages = ["English", "Spanish", "French", "Portuguese", "(other)"];

function getRandomNumber(min: number, max: number) {
  // min and max included
  return Math.floor(window.agRandom() * (max - min + 1) + min);
}

function getValueFromServer(
  params: RichCellEditorValuesCallbackParams,
): Promise<string[]> {
  const search = params.search?.toLowerCase() ?? "";
  // Simulates an async request to a server
  return new Promise((resolve) => {
    console.log(`Grid requested \`${search}\` from server.`);
    setTimeout(() => {
      const entries = languages.filter((l) => l.toLowerCase().includes(search));
      console.log(
        `Server response for \`${search}\`: ${entries.length} hit${entries.length === 1 ? "" : "s"}.`,
      );
      resolve(entries);
    }, 1000);
  });
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<ColDef[]>([
      {
        field: "language",
        cellEditor: "agRichSelectCellEditor",
        width: 300,
        cellEditorParams: {
          allowTyping: true,
          filterList: true,
          values: getValueFromServer,
          filterListAsync: true,
        } as IRichCellEditorParams,
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 200,
      editable: true,
    });
    const rowData = ref<any[] | null>(
      new Array(100)
        .fill(null)
        .map(() => ({ language: languages[getRandomNumber(0, 4)] })),
    );

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Rich Select Async Filtering](https://www.ag-grid.com/examples/provided-cell-editors-rich-select-async/rich-select-full-async-values/vue3)

```js
columnDefs: [
    {
        field: 'language',
        cellEditor: 'agRichSelectCellEditor',
        cellEditorParams: {
            allowTyping: true,
            filterList: true,
            filterListAsync: true,
            values: (params) => {
                return fetch(`/api/languages?search=${encodeURIComponent(params.search)}`)
                    .then((res) => res.json())
                    .then((data) => data.items);
            }
        }
    }
]
```

## Paged Async Filtering

For very large, server-backed datasets, combine async filtering with `valuesPage` so filtered results are also loaded incrementally. `valuesPageInitialStartRow` is only used for the initial unfiltered load; once the user types, filtered paging starts from row `0`.

In this mode each request includes:

- `search` for the current filter text.
- `startRow` and `endRow` for range-based pagination.
- `cursor` (optional) for cursor-based forward pagination APIs (`undefined` on the first request, then replayed from the previous response).

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `valuesPage` | `RichCellEditorValuesPageCallback` |  |  | Optional paged datasource for very large value lists. When provided, values are loaded incrementally and additional pages are requested as the user scrolls. If both `values` and `valuesPage` are set, `valuesPage` takes precedence. |
| `valuesPageInitialStartRow` | `number \| RichCellEditorValuesPageStartRowCallback` |  | `0` | Initial page start row when using `valuesPage`. Can be a fixed number or a callback that derives the start row from the current editor value. Only applied for the initial, unfiltered load. Filtered searches always start from row `0`. |
| `valuesPageSize` | `number` |  | `100` | Number of rows requested per page when using `valuesPage`. |
| `valuesPageLoadThreshold` | `number` |  | `10` | Number of rows from the end of the loaded list at which the next page is requested. |
| `filterListAsync` | `boolean` |  | `false` | Set to `true` to enable asynchronous filtering of values via the `values` or `valuesPage` callback. (only relevant when `allowTyping=true` and `filterList=true`). |

#### Rich Select Paged Async Filtering

```ts
import {
  createApp,
  defineComponent,
  onBeforeMount,
  ref,
  shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GridApi,
  GridOptions,
  GridReadyEvent,
  IRichCellEditorParams,
  ModuleRegistry,
  RichCellEditorValuesPageParams,
  RichCellEditorValuesPageResult,
  TextEditorModule,
  enableDevValidations,
} from "ag-grid-community";
import { RichSelectModule } from "ag-grid-enterprise";
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  TextEditorModule,
  ClientSideRowModelModule,
  RichSelectModule,
]);

const languages = new Array(20000)
  .fill(null)
  .map((_, index) => `Language ${index + 1}`);

function getRandomNumber(min: number, max: number) {
  // min and max included
  return Math.floor(window.agRandom() * (max - min + 1) + min);
}

function getFilteredValuePageFromServer(
  params: RichCellEditorValuesPageParams,
): Promise<RichCellEditorValuesPageResult<string>> {
  const search = params.search.toLowerCase();
  // Simulates an async request to a server
  return new Promise((resolve) => {
    setTimeout(() => {
      const filtered = search
        ? languages.filter((language) =>
            language.toLowerCase().includes(search),
          )
        : languages;
      const pageValues = filtered.slice(params.startRow, params.endRow);
      const nextOffset =
        params.endRow < filtered.length ? String(params.endRow) : null;
      resolve({
        values: pageValues,
        lastRow: filtered.length,
        cursor: nextOffset,
      });
    }, 300);
  });
}

function getInitialStartRowForValue(value: string | null | undefined): number {
  if (!value) {
    return 0;
  }
  const match = /^Language (\d+)$/.exec(value);
  if (!match) {
    return 0;
  }
  const selectedIndex = Number(match[1]) - 1;
  return Math.max(selectedIndex - 40, 0);
}

const VueExample = defineComponent({
  template: `
        <div style="height: 100%">
                <ag-grid-vue
      style="width: 100%; height: 100%;"
      @grid-ready="onGridReady"
      :columnDefs="columnDefs"
      :defaultColDef="defaultColDef"
      :rowData="rowData"></ag-grid-vue>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  setup(props) {
    const gridApi = shallowRef<GridApi | null>(null);
    const columnDefs = ref<ColDef[]>([
      {
        field: "language",
        width: 320,
        editable: true,
        cellEditor: "agRichSelectCellEditor",
        cellEditorParams: {
          allowTyping: true,
          filterList: true,
          filterListAsync: true,
          valuesPage: getFilteredValuePageFromServer,
          valuesPageInitialStartRow: (value: string | null | undefined) =>
            getInitialStartRowForValue(value),
          valuesPageSize: 80,
          valuesPageLoadThreshold: 8,
        } as IRichCellEditorParams,
      },
    ]);
    const defaultColDef = ref<ColDef>({
      width: 220,
      editable: true,
    });
    const rowData = ref<any[] | null>(
      new Array(100)
        .fill(null)
        .map(() => ({
          language: languages[getRandomNumber(0, languages.length - 1)],
        })),
    );

    const onGridReady = (params: GridReadyEvent) => {
      gridApi.value = params.api;
    };

    return {
      gridApi,
      columnDefs,
      defaultColDef,
      rowData,
      onGridReady,
    };
  },
});

const app = createApp(VueExample);
app.mount("#app");
```

[Live example: Rich Select Paged Async Filtering](https://www.ag-grid.com/examples/provided-cell-editors-rich-select-async/rich-select-paged-async-filtering/vue3)

```js
columnDefs: [
    {
        cellEditor: 'agRichSelectCellEditor',
        cellEditorParams: {
            allowTyping: true,
            filterList: true,
            filterListAsync: true,
            valuesPageInitialStartRow: (value) => getRowForSelectedValue(value),
            valuesPage: (params) => {
                    return fetch(
                        `/api/languages
                            ?search=${encodeURIComponent(params.search)}
                            &startRow=${params.startRow}
                            &endRow=${params.endRow}`
                    ).then((res) => res.json());
            },
        }
    }
]
```

## API

Properties available on the `IRichCellEditorParams&lt;TData = any, TValue = any, GValue = any&gt;` interface.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `values` | [`TValue[] \| RichCellEditorValuesCallback`](https://www.ag-grid.com/vue-data-grid/typescript-generics/#cell-value-tvalue) |  |  | The list of values to be selected from. Required when `valuesPage` is not provided. |
| `valuesPage` | `RichCellEditorValuesPageCallback` |  |  | Optional paged datasource for very large value lists. When provided, values are loaded incrementally and additional pages are requested as the user scrolls. If both `values` and `valuesPage` are set, `valuesPage` takes precedence. |
| `valuesPageInitialStartRow` | `number \| RichCellEditorValuesPageStartRowCallback` |  | `0` | Initial page start row when using `valuesPage`. Can be a fixed number or a callback that derives the start row from the current editor value. Only applied for the initial, unfiltered load. Filtered searches always start from row `0`. |
| `valuesPageSize` | `number` |  | `100` | Number of rows requested per page when using `valuesPage`. |
| `valuesPageLoadThreshold` | `number` |  | `10` | Number of rows from the end of the loaded list at which the next page is requested. |
| `cellHeight` | `number` |  |  | The row height, in pixels, of each value. |
| `cellRenderer` | `any` |  |  | The cell renderer to use to render each value. Cell renderers are useful for rendering rich HTML values, or when processing complex data. |
| `cellRendererParams` | `any` |  |  | The custom parameters to be used by the cell render. |
| `allowTyping` | `boolean` |  | `false` | Set to `true` to be able to type values in the display area. |
| `filterList` | `boolean` |  | `false` | If `true` it will filter the list of values as you type (only relevant when `allowTyping=true`). |
| `filterListAsync` | `boolean` |  | `false` | Set to `true` to enable asynchronous filtering of values via the `values` or `valuesPage` callback. (only relevant when `allowTyping=true` and `filterList=true`). |
| `searchType` | `'match' \| 'matchAny' \| 'fuzzy'` |  | `'fuzzy'` | The type of search algorithm that is used when searching for values. `match` - Matches if the value starts with the text typed. `matchAny` - Matches if the value contains the text typed. `fuzzy` - Matches the closest value to text typed. Note: When a cellRenderer is specified, this option will not work. |
| `highlightMatch` | `boolean` |  | `false` | If `true`, each item on the list of values will highlight the part of the text that matches the input. Note: It only makes sense to use this option when `filterList` is `true` and `searchType` is **not** `fuzzy`. |
| `multiSelect` | `boolean` |  |  | If `true` this component will allow multiple items from the list of values to be selected. |
| `suppressDeselectAll` | `boolean` |  |  | If `true` the option to remove all selected options will not be displayed. Note: This feature only works when `multiSelect=true`. |
| `suppressMultiSelectPillRenderer` | `boolean` |  |  | When `multiSelect=true` the editor will automatically show the selected items as "pills". Set this property to `true` suppress this behaviour. |
| `searchDebounceDelay` | `number` |  | `300` | The value in `ms` for the search algorithm debounce delay |
| `valuePlaceholder` | `string` |  |  | A string value to be used when no value has been selected. |
| `valueListGap` | `number` |  | `4` | The space in pixels between the value display and the list of items. |
| `valueListMaxHeight` | `number \| string` |  | `'calc(var(--ag-row-height) * 6.5)'` | The maximum height of the list of items. If the value is a `number` it will be treated as pixels, otherwise it should be a valid CSS size string. |
| `valueListMaxWidth` | `number \| string` |  |  | The maximum width of the list of items. If the value is a `number` it will be treated as pixels, otherwise it should be a valid CSS size string. Default: Width of the cell being edited. |
| `formatValue` | `Function` |  |  | A callback function that allows you to change the displayed value for simple data. |
| `parseValue` | `Function` |  |  | A callback function that allows you to convert the value of the Rich Select Editor to the data format of the Grid Column when they are different. |
