---
metaTitle: SelectObject component | AwesCode UI
meta:
  - name: description
    content: The &lt;AwSelectObject /&gt; component provides an advanced select dropdown for object-based options with search, AJAX, and grouping - UI Vue component for AwesCode UI.
title: SelectObject
---

# AwSelectObject

**Category:** Molecule | **Import:** Global

The `AwSelectObject` component is an advanced select dropdown specifically designed for working with object-based options. It supports local and AJAX data sources, search functionality, infinite scrolling, grouping, and mobile-optimized interfaces.

## Overview

`AwSelectObject` provides advanced select functionality with:
- Object-based options with customizable key/label extraction
- Local options or AJAX data fetching
- Search/filter with debouncing
- Infinite scroll pagination (AJAX)
- Option grouping
- Mobile-optimized dropdown
- Keyboard navigation
- Custom option rendering
- Preload strategies
- Not found handling

## Usage

### Basic Example

```markup
<AwSelectObject
  v-model="selectedUser"
  :options="users"
  label="Select User"
  option-label="name"
  track-by="id"
/>
```

### With Object Options

```markup
<template>
  <AwSelectObject
    v-model="selectedCountry"
    :options="countries"
    label="Country"
    option-label="name"
    track-by="code"
  />
</template>

<script>
export default {
  data() {
    return {
      selectedCountry: null,
      countries: [
        { code: 'US', name: 'United States', region: 'North America' },
        { code: 'CA', name: 'Canada', region: 'North America' },
        { code: 'UK', name: 'United Kingdom', region: 'Europe' }
      ]
    }
  }
}
</script>
```

### With AJAX Data Source

```markup
<AwSelectObject
  v-model="selectedUser"
  :options="fetchUsers"
  label="Search Users"
  option-label="full_name"
  track-by="id"
  searchable
  preload="mounted"
/>

<script>
export default {
  methods: {
    fetchUsers(search, page) {
      return {
        url: '/api/users',
        params: { search, page }
      }
    }
  }
}
</script>
```

### With Function-Based Label

```markup
<AwSelectObject
  v-model="selectedUser"
  :options="users"
  :option-label="user => `${user.first_name} ${user.last_name} (${user.email})`"
  track-by="id"
  label="User"
/>
```

### With Grouping

```markup
<AwSelectObject
  v-model="selectedCountry"
  :options="countries"
  label="Country"
  option-label="name"
  track-by="code"
  group-by="region"
/>
```

### With Custom Option Rendering

```markup
<AwSelectObject
  v-model="selectedUser"
  :options="users"
  label="User"
  option-label="name"
  track-by="id"
>
  <template #option-label="{ option, searchPhrase, highlightSearch }">
    <div class="flex items-center gap-2">
      <img :src="option.avatar" class="w-8 h-8 rounded-full" />
      <div>
        <div v-html="highlightSearch(option.name)"></div>
        <div class="text-sm text-gray-500">{{ option.email }}</div>
      </div>
    </div>
  </template>
</AwSelectObject>
```

### People selector pattern (users/customers with avatars)

Use the `icon` and `option-label` slots together to show avatars (or a fallback icon) for person-like entities such as users or customers.

```markup
<AwSelectObject
  v-model="selectedPerson"
  :options="searchPeople"
  :option-label="person => `${person.first_name} ${person.last_name}`"
  track-by="id"
  clearable
>
  <template #icon="{ option }">
    <AwAvatar
      v-if="option"
      class="mx-3"
      :src="option.avatar"
      :name="`${option.first_name} ${option.last_name}`"
      size="24"
      icon="awesio/user"
    />
    <AwActionIcon
      v-else
      class="mx-3 rounded-full"
      icon="awesio/user"
      size="xs"
    />
  </template>

  <template #option-label="{ option, highlightSearch }">
    <div class="flex items-center gap-2">
      <AwAvatar
        :src="option.avatar"
        :name="`${option.first_name} ${option.last_name}`"
        size="24"
        class="-ml-1"
        icon="awesio/user"
      />
      <div class="leading-tight">
        <div v-html="highlightSearch(`${option.first_name} ${option.last_name}`)" />
        <div v-if="option.email" class="text-sm text-mono-500">
          {{ option.email }}
        </div>
      </div>
    </div>
  </template>
</AwSelectObject>

<script>
export default {
  methods: {
    searchPeople(search, page) {
      return {
        url: '/api/users',
        params: { search, page }
      }
    }
  }
}
</script>
```

## API

### Props

| Name | Description | Type | Required | Default |
|------|-------------|------|----------|---------|
| value | Selected value (v-model) | `Any` | `false` | `null` |
| options | Options array or AJAX function | `Array\|Function` | `false` | `[]` |
| label | Input label | `String` | `false` | `''` |
| optionLabel | Key or function to extract label from option | `String\|Function` | `false` | `''` |
| trackBy | Key to use as value (required for objects) | `String` | `false` | `''` |
| groupBy | Key to group options | `String` | `false` | `''` |
| searchable | Enable search/filter | `Boolean` | `false` | `true` |
| clearable | Show clear button | `Boolean` | `false` | `false` |
| disabled | Disable select | `Boolean` | `false` | `false` |
| maxSearchItems | Max items to show | `Number` | `false` | `100` |
| debounce | Search debounce delay (ms) | `Number` | `false` | `400` |
| preload | Preload AJAX data ('mounted', 'focus', or both) | `String\|Object` | `false` | `'focus'` |
| showDropdownInput | Always show dropdown input | `Boolean` | `false` | `false` |
| getResponseData | Extract options from AJAX response | `Function` | `false` | `data => data.data` |
| getNextPage | Calculate next page from response | `Function` | `false` | See source |
| desktopFrom | Breakpoint for desktop mode | `String` | `false` | `'md'` |
| isCaret | Show caret icon | `Boolean\|Function` | `false` | `true` |
| isNotFound | Override not found detection | `Boolean` | `false` | `null` |
| optionDisabled | Function to determine if option is disabled | `Function` | `false` | `() => false` |

### Slots

| Name | Description | Props | Default Slot Content |
|------|-------------|-------|---------------------|
| default | Replace entire component | - | - |
| toggler | Custom input toggle | `{ option, optionLabel, isMobile, isOpened, isLoading, applySearch, open, close, clear }` | AwSelectInput |
| prefix | Content before input | - | - |
| postfix | Content after input | - | - |
| icon | Custom icon in input | `{ option, optionLabel, isLoading, isOpened }` | - |
| element | Custom input element | `{ value }` | - |
| caret | Custom caret icon | `{ isOpened }` | Default caret |
| dropdown | Replace entire dropdown | `{ optionsList, isOpened }` | Default dropdown |
| dropdown-input | Custom dropdown input | `{ option, optionLabel, isMobile, isOpened, isLoading }` | AwSelectInput |
| dropdown-before | Content before options | `{ optionsList, isLoading, hasSearch, searchPhrase, close }` | - |
| dropdown-after | Content after options | `{ optionsList, isLoading }` | - |
| option-label | Custom option label | `{ option, optionLabel, optionValue, index, active, disabled, searchPhrase, highlightSearch, close }` | Highlighted label |
| not-found | Custom not found message | `{ searchPhrase }` | "Nothing found" |
| loading | Custom loading message | - | "Loading..." |

### Events

| Name | Payload | Description |
|------|---------|-------------|
| input | `value` | Emitted when selection changes (v-model) |
| search | `searchPhrase` | Emitted when search text changes |
| open | `{ target }` | Emitted when dropdown opens |
| close | `{ target }` | Emitted when dropdown closes |
| focus | `{ target }` | Emitted when input is focused |
| clear | - | Emitted when clear button is clicked |
| not-found | `searchPhrase` | Emitted when Enter pressed on empty results |

## AJAX Data Loading

### Basic AJAX Setup

```markup
<AwSelectObject
  v-model="selected"
  :options="loadOptions"
  label="Search"
  option-label="name"
  track-by="id"
/>

<script>
export default {
  methods: {
    loadOptions(search, page) {
      // Return axios config object or URL string
      return {
        url: '/api/items',
        params: { search, page, per_page: 20 }
      }
    }
  }
}
</script>
```

### Custom Response Parsing

```markup
<AwSelectObject
  v-model="selected"
  :options="loadOptions"
  :get-response-data="response => response.items"
  :get-next-page="(response, currentPage) => response.has_more ? currentPage + 1 : 0"
  option-label="name"
  track-by="id"
/>
```

### Preload Strategies

```markup
<!-- Preload on mount -->
<AwSelectObject
  :options="loadOptions"
  preload="mounted"
/>

<!-- Preload on focus -->
<AwSelectObject
  :options="loadOptions"
  preload="focus"
/>

<!-- Preload both -->
<AwSelectObject
  :options="loadOptions"
  preload="mounted focus"
/>

<!-- Advanced preload config -->
<AwSelectObject
  :options="loadOptions"
  :preload="{ mounted: true, focus: false }"
/>
```

## Grouping Options

```markup
<template>
  <AwSelectObject
    v-model="selectedFood"
    :options="foods"
    label="Food"
    option-label="name"
    track-by="id"
    group-by="category"
  />
</template>

<script>
export default {
  data() {
    return {
      foods: [
        { id: 1, name: 'Apple', category: 'Fruits' },
        { id: 2, name: 'Banana', category: 'Fruits' },
        { id: 3, name: 'Carrot', category: 'Vegetables' },
        { id: 4, name: 'Broccoli', category: 'Vegetables' }
      ]
    }
  }
}
</script>
```

## Mobile vs Desktop Behavior

### Desktop (md+)
- Popper-based dropdown positioned below input
- Search input inline
- Click outside to close

### Mobile (< md)
- Full-screen modal dropdown
- Body scroll locked
- Search input at top of modal
- Back button to close

## Keyboard Navigation

- **Arrow Up/Down**: Navigate options
- **Enter**: Select focused option
- **Escape**: Close dropdown
- **Type**: Search/filter options

## Related Components

- `AwSelect` - Simple select for string arrays
- `AwSelectNative` - Native HTML select
- `AwDropdownButton` - Used for options rendering

## Notes

- **Import Method:** Global - Available as molecule component
- Requires `track-by` prop when options are objects
- AJAX mode activated when `options` is a function
- Infinite scroll automatically loads next page when scrolling near bottom
- Search phrase highlights matched text in bold
- Mobile dropdown uses body-scroll-lock to prevent background scrolling
- Popper.js used for desktop dropdown positioning
- Debounced search prevents excessive API calls
- Supports IntersectionObserver for infinite scroll
- Can be used with Axios cancel tokens for request cancellation
