---
title: "Rich Select Cell Editor - Async Values"
enterprise: true
framework: angular
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/angular-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 { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
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,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    {
      field: "language",
      cellEditor: "agRichSelectCellEditor",
      cellEditorParams: {
        values: getValueFromServer,
      } as IRichCellEditorParams,
    },
  ];
  defaultColDef: ColDef = {
    width: 200,
    editable: true,
  };
  rowData: any[] | null = new Array(100)
    .fill(null)
    .map(() => ({ language: languages[getRandomNumber(0, 4)] }));
}

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);
  });
}
```

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

```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 { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
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,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
  /> `,
})
export class AppComponent {
  columnDefs: 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,
    },
  ];
  defaultColDef: ColDef = {
    width: 220,
    editable: true,
  };
  rowData: any[] | null = new Array(100)
    .fill(null)
    .map(() => ({
      language: languages[getRandomNumber(0, languages.length - 1)],
    }));
}

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);
}
```

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

```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/angular-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 { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
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,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
  /> `,
})
export class AppComponent {
  columnDefs: ColDef[] = [
    {
      field: "language",
      cellEditor: "agRichSelectCellEditor",
      width: 300,
      cellEditorParams: {
        allowTyping: true,
        filterList: true,
        values: getValueFromServer,
        filterListAsync: true,
      } as IRichCellEditorParams,
    },
  ];
  defaultColDef: ColDef = {
    width: 200,
    editable: true,
  };
  rowData: any[] | null = new Array(100)
    .fill(null)
    .map(() => ({ language: languages[getRandomNumber(0, 4)] }));
}

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);
  });
}
```

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

```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 { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
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,
]);

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<ag-grid-angular
    style="width: 100%; height: 100%;"
    [columnDefs]="columnDefs"
    [defaultColDef]="defaultColDef"
    [rowData]="rowData"
  /> `,
})
export class AppComponent {
  columnDefs: 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,
    },
  ];
  defaultColDef: ColDef = {
    width: 220,
    editable: true,
  };
  rowData: any[] | null = new Array(100)
    .fill(null)
    .map(() => ({
      language: languages[getRandomNumber(0, languages.length - 1)],
    }));
}

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);
}
```

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

```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/angular-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. |
