# LookupField

```ts
import { LookupField } from 'cx/widgets';
```



The `LookupField` widget offers selection from a list of available options. It's similar to the native HTML `select` element but provides additional features:

- Searching/filtering the list
- Querying remote data
- User-friendly multiple selection with tags

```tsx
import { createModel } from "cx/data";
import { bind, LabelsTopLayout } from "cx/ui";
import { LookupField } from "cx/widgets";

interface Model {
  selectedId: number;
  selectedText: string;
  selectedRecords: { id: number; text: string }[];
}

const m = createModel<Model>();

const options = [
  { id: 1, text: "Apple" },
  { id: 2, text: "Banana" },
  { id: 3, text: "Cherry" },
  { id: 4, text: "Date" },
  { id: 5, text: "Elderberry" },
];

export default (
  <LabelsTopLayout columns={2}>
    <LookupField
      label="Single Select"
      value={bind(m.selectedId, 1)}
      text={bind(m.selectedText, "Apple")}
      options={options}
    />
    <LookupField
      label="Multiple Select"
      records={m.selectedRecords}
      options={options}
      multiple
      icon="search"
    />
  </LabelsTopLayout>
);

```

## Remote Data

Use `onQuery` to fetch options from a server. The callback receives the search query and should return a list of options or a Promise.

Use `fetchAll` to fetch all data once and filter client-side, which is more efficient for moderate-sized datasets. Add `cacheAll` to keep the fetched data cached for the widget's lifetime, avoiding refetches when the dropdown reopens.

```tsx
import { createModel } from "cx/data";
import { LabelsTopLayout } from "cx/ui";
import { getSearchQueryPredicate } from "cx/util";
import { LookupField } from "cx/widgets";

interface Model {
  cityId: number;
  cityText: string;
  cities: { id: number; text: string }[];
}

const m = createModel<Model>();

const cities = ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Diego", "Dallas"].map(
  (text, id) => ({ id, text }),
);

function queryCity(query: string) {
  let predicate = getSearchQueryPredicate(query);
  return new Promise<typeof cities>((resolve) => {
    setTimeout(() => resolve(cities.filter((x) => predicate(x.text))), 300);
  });
}

export default (
  <LabelsTopLayout columns={2}>
    <LookupField
      label="Query on Each Keystroke"
      value={m.cityId}
      text={m.cityText}
      onQuery={queryCity}
      minQueryLength={2}
      placeholder="Type at least 2 chars..."
    />
    <LookupField
      label="Fetch Once, Filter Locally"
      records={m.cities}
      onQuery={queryCity}
      fetchAll
      cacheAll
      multiple
      placeholder="Type to filter..."
    />
  </LabelsTopLayout>
);

```

## Data Binding

| Selection Mode | Local (options)              | Remote (onQuery)       |
| -------------- | ---------------------------- | ---------------------- |
| Single         | `value` and/or `text`        | `value` and `text`     |
| Multiple       | `values` and/or `records`    | `records`              |

## Examples

- [Custom Bindings](/docs/examples/lookup-custom-bindings) - Passing additional fields to the selection
- [Infinite Lists](/docs/examples/lookup-infinite-list) - Handling large datasets with pagination
- [Options Filter](/docs/examples/lookup-options-filter) - Filtering options based on external criteria
- [Options Grouping](/docs/examples/lookup-options-grouping) - Grouping options in the dropdown

## Configuration

### Core Properties

| Property         | Type       | Default | Description                                               |
| ---------------- | ---------- | ------- | --------------------------------------------------------- |
| `value`          | `any`      |         | Selected value ID (single mode)                           |
| `text`           | `string`   |         | Selected value text (single mode)                         |
| `values`         | `array`    |         | Array of selected IDs (multiple mode)                     |
| `records`        | `array`    |         | Array of selected records (multiple mode)                 |
| `options`        | `array`    |         | Array of available options                                |
| `multiple`       | `boolean`  | `false` | Enable multiple selection                                 |
| `placeholder`    | `string`   |         | Placeholder text when empty                               |
| `disabled`       | `boolean`  | `false` | Disables the field                                        |
| `readOnly`       | `boolean`  | `false` | Makes the field read-only                                 |

### Option Fields

| Property          | Type     | Default  | Description                                      |
| ----------------- | -------- | -------- | ------------------------------------------------ |
| `optionIdField`   | `string` | `"id"`   | Field name for option ID                         |
| `optionTextField` | `string` | `"text"` | Field name for option display text               |
| `valueIdField`    | `string` | `"id"`   | Field name for storing ID in selection           |
| `valueTextField`  | `string` | `"text"` | Field name for storing text in selection         |

### Query Options

| Property          | Type       | Default | Description                                                 |
| ----------------- | ---------- | ------- | ----------------------------------------------------------- |
| `onQuery`         | `function` |         | `(query, instance) => options[]` - Fetch options            |
| `queryDelay`      | `number`   | `150`   | Delay in ms before query is executed                        |
| `minQueryLength`  | `number`   | `0`     | Minimum characters before query is made                     |
| `fetchAll`        | `boolean`  | `false` | Fetch all options once, filter client-side                  |
| `cacheAll`        | `boolean`  | `false` | Cache fetched options for widget lifetime                   |
| `infinite`        | `boolean`  | `false` | Enable infinite scrolling for large datasets                |
| `pageSize`        | `number`   | `100`   | Number of items per page in infinite mode                   |

### Appearance

| Property                   | Type      | Default | Description                                        |
| -------------------------- | --------- | ------- | -------------------------------------------------- |
| `icon`                     | `string`  |         | Icon displayed on the left side                    |
| `showClear`                | `boolean` | `true`  | Shows clear button when value is present           |
| `hideClear`                | `boolean` | `false` | Hides the clear button                             |
| `alwaysShowClear`          | `boolean` | `false` | Shows clear button even when required              |
| `hideSearchField`          | `boolean` | `false` | Hide the search input in dropdown                  |
| `minOptionsForSearchField` | `number`  | `7`     | Minimum options before search field is shown       |

### Behavior

| Property         | Type      | Default | Description                                           |
| ---------------- | --------- | ------- | ----------------------------------------------------- |
| `closeOnSelect`  | `boolean` | `true`  | Close dropdown after selection                        |
| `autoOpen`       | `boolean` | `false` | Open dropdown on focus                                |
| `quickSelectAll` | `boolean` | `false` | Allow Ctrl+A to select all visible options            |
| `sort`           | `boolean` | `false` | Sort dropdown options alphabetically                  |

### Messages

| Property                    | Type     | Default                                  | Description                       |
| --------------------------- | -------- | ---------------------------------------- | --------------------------------- |
| `loadingText`               | `string` | `"Loading..."`                           | Text shown while loading          |
| `noResultsText`             | `string` | `"No results found."`                    | Text when no options match        |
| `queryErrorText`            | `string` | `"Error occurred while querying..."`     | Text on query error               |
| `minQueryLengthMessageText` | `string` | `"Type in at least {0} character(s)."` | Text when query is too short      |