Choices

This component is intended to be used to build complex selection interfaces for single and multiple values. It also supports search on frontend or server, when dealing with large lists.

Pro tip: Most of time you just need a simple Select component, which renders nice natively on all devices.

Selection

By default, it will look up for:

  • $object->id for option value.
  • $object->name for option display label.
  • $object->avatar for avatar picture.

Single
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

Multiple
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

Custom options
It has custom options
No results found.
rohan.eliane
South Luciano

joseph04
Micaelaland

wullrich
West Clementine

glover.jermey
Pfannerstillport

bennie54
New Kiera

reinhold58
Elisefurt

bernard34
Stantonstad

fahey.alysa
Bethside

@php
$users = $this->users;
@endphp
{{-- Notice `single` --}}
<x-choices label="Single" wire:model="user_id" :options="$users" single clearable />
{{-- public array $users_multi_ids = []; --}}
<x-choices label="Multiple" wire:model="users_multi_ids" :options="$users" clearable />
{{-- Custom options --}}
<x-choices
label="Custom options"
wire:model="user_custom_id"
:options="$users"
option-label="username"
option-sub-label="city.name"
option-avatar="other_avatar"
icon="o-users"
height="max-h-96" {{-- Default is `max-h-64` --}}
hint="It has custom options"
single />

Select All

This option only works for multiple and non-searchable exclusively.


Multiple
Select all
Remove all
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

Multiple
Select all stuff
Delete all things
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

@php
$users = $this->users;
@endphp
{{-- Notice `allow-all` --}}
<x-choices label="Multiple" wire:model="users_all_ids" :options="$users" allow-all />
<x-choices
label="Multiple"
wire:model="users_all2_ids"
:options="$users"
allow-all
allow-all-text="Select all stuff"
remove-all-text="Delete all things" />

Compact mode

This option only works for multiple and non-searchable exclusively.


Compact
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

Compact label
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

@php
$users = $this->users;
@endphp
{{-- Notice `compact` --}}
<x-choices label="Compact" wire:model="users_compact_ids" :options="$users" compact />
<x-choices
label="Compact label"
wire:model="users_compact2_ids"
:options="$users"
compact
compact-text="stuff chosen" />

You can combine allow-all and compact

Select All + Compact
Select all
Remove all
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

@php
$users = $this->users;
@endphp
<x-choices
label="Select All + Compact"
wire:model="users_all_compact_ids"
:options="$users"
compact
allow-all />

Searchable (frontend)

If you judge you don't have a huge list of items, you can make it searchable offline on "frontend side". But, if you have a huge list it is a better idea to make it searchable on "server side", otherwise you can face some slow down on frontend. See on the next section.


Single (frontend)
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

Multiple (frontend)
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

@php
$users = $this->users;
@endphp
{{-- Notice `searchable` --}}
{{-- Notice this is a different component, but with same API --}}
<x-choices-offline
label="Single (frontend)"
wire:model="user_searchable_offline_id"
:options="$users"
placeholder="Search ..."
single
clearable
searchable />
<x-choices-offline
label="Multiple (frontend)"
wire:model="users_multi_searchable_offline_ids"
:options="$users"
placeholder="Search ..."
clearable
searchable />

Searchable (server)

When dealing with large options list use searchable parameter. By default, it calls search() method to get fresh options from "server side" while typing. You can change the method's name by using search-function parameter.


Searchable + Single
No results found.
Aaliyah

Adah

Adelle

Alanna

Alivia

Searchable + Multiple
Ops! Nothing here ...
Aaliyah

Adah

Adelle

Alanna

Alivia

@php
$usersSearchable = $this->usersSearchable;
$usersMultiSearchable = $this->usersMultiSearchable;
@endphp
{{-- Notice `searchable` + `single` --}}
<x-choices
label="Searchable + Single"
wire:model="user_searchable_id"
:options="$usersSearchable"
placeholder="Search ..."
single
searchable />
{{-- Notice custom `search-function` --}}
<x-choices
label="Searchable + Multiple"
wire:model="users_multi_searchable_ids"
:options="$usersMultiSearchable"
placeholder="Search ..."
search-function="searchMulti"
no-result-text="Ops! Nothing here ..."
no-progress
searchable />

You must also consider displaying pre-selected items on list, when it first renders and while searching. There are many approaches to make it work, but here is an example for single search.

new class extends Component {
// Selected option
public ?int $user_searchable_id = null;
// Options list
public Collection $usersSearchable;
public function mount()
{
// Fill options when component first renders
$this->search();
}
// Also called as you type
public function search(string $value = '')
{
// Besides the search results, you must include on demand selected option
$selectedOption = User::where('id', $this->user_searchable_id)->get();
$this->usersSearchable = User::query()
->where('name', 'like', "%$value%")
->take(5)
->orderBy('name')
->get()
->merge($selectedOption); // <-- Adds selected option
}
}

Sometimes you don't want to hit a datasource on every keystroke. So, you can make use of debounce to control over how often a network request is sent.

Combine it with min-chars attribute to avoid hit search method itself until you have typed such amount of chars.

Searchable + Single + Debounce + Min chars
Type at least 2 chars
No results found.
@php
$usersSearchableMinChars = $this->usersSearchableMinChars;
@endphp
{{-- Notice `min-chars` and `debounce` --}}
<x-choices
label="Searchable + Single + Debounce + Min chars"
wire:model="user_searchable_min_chars_id"
:options="$usersSearchableMinChars"
search-function="searchMinChars"
debounce="300ms" {{-- Default is `250ms` --}}
min-chars="2" {{-- Default is `0` --}}
hint="Type at least 2 chars"
single
searchable />

You can pass any extra arbitrary search parameters like this.

{{-- Notice `search-function` with extra arbitrary parameters --}}
<x-choices
label="Extra parameters"
wire:model="user_id"
:options="$users"
search-function="searchExtra(123, 'thing')"
searchable />
public function search(string $value = '', int $extra1 = 0, string $extra2 = '')
{
// The first parameter is the default and comes from the search input.
}

Slots

You have full control on rendering items by using the @scope('item', $object) special blade directive. It injects the current $object from the loop's context and achieves the same behavior that you would expect from the Vue/React scoped slots.

You can customize the list item and selected item slot. Searchable (online) works with blade syntax.

Slots (online)
No results found.
Cletus
Aut aut dolorem laborum deleniti. Natus aut odio dolorem necessitatibus. Suscipit fuga aut rem.
rohan.eliane

Dashawn
Ullam aperiam voluptatem dicta et est consequatur voluptas. Cumque eaque sit quod debitis vel impedit et in. Mollitia et dolorum aut ullam debitis quia. Exercitationem magni non consequatur amet est dolorem. Illo tenetur rerum autem cupiditate perspiciatis.
joseph04

Kip
Animi possimus dolores cum harum et laboriosam placeat nulla. Sapiente unde sint laboriosam debitis temporibus amet. Reprehenderit quis aut quidem facilis mollitia. Praesentium illum sit ut nobis qui. A doloremque ipsa nihil omnis quibusdam aut.
wullrich

Myah
Qui saepe ipsum hic ut non fugit. Perspiciatis exercitationem enim quisquam qui pariatur animi quasi. Suscipit amet minus ut impedit consequatur nesciunt. Sit quia error et saepe.
glover.jermey

Eliza
Incidunt aperiam in amet commodi adipisci et quia. Quod impedit quo excepturi minus. Voluptas nesciunt aut molestiae esse est harum in. Et qui ea saepe.
bennie54

Hilbert
Nam quaerat ut aut mollitia illum. Asperiores et saepe vel corporis architecto odio saepe. Eius aut sed mollitia itaque. Suscipit quaerat dolorem sint nemo.
reinhold58

Clementina
Aut quae magnam voluptas voluptatem nemo. Et laudantium praesentium a qui velit sint voluptas. Atque doloribus fugit odio porro voluptates eos. Ratione temporibus placeat qui deserunt voluptate iste.
bernard34

Keyshawn
Facere a optio sit perferendis mollitia modi. Beatae id aut alias autem. Velit at ut voluptatem facilis eos.
fahey.alysa

@php
$users = $this->users;
@endphp
<x-choices label="Slots (online)" wire:model="user_custom_slot_id" :options="$users" single>
{{-- Item slot --}}
@scope('item', $user)
<x-list-item :item="$user" sub-value="bio">
<x-slot:avatar>
<x-icon name="o-user" class="bg-primary/10 p-2 w-9 h-9 rounded-full" />
</x-slot:avatar>
<x-slot:actions>
<x-badge :value="$user->username" class="badge-soft badge-primary badge-sm" />
</x-slot:actions>
</x-list-item>
@endscope
{{-- Selection slot--}}
@scope('selection', $user)
{{ $user->name }} ({{ $user->username }})
@endscope
</x-choices>

You can append or prepend anything like this. Make sure to use appropriated css round class on left or right.

Slots
No results found.
Cletus

Dashawn

Kip

Myah

Eliza

Hilbert

Clementina

Keyshawn

@php
$users = $this->users
@endphp
<x-choices label="Slots" wire:model="user_custom_slot_id" :options="$users" single>
<x-slot:prepend>
{{-- Add `join-item` to all prepended elements --}}
<x-button icon="o-trash" class="join-item" />
</x-slot:prepend>
<x-slot:append>
{{-- Add `join-item` to all appended elements --}}
<x-button label="Create" icon="o-plus" class="join-item btn-primary" />
</x-slot:append>
</x-choices>

Note about large numbers

This component uses the options id values to handle selection. It tries to determine if these values are a int or string.

But, due to Javascript limitation with large numbers like these below, it will break.

public array $options = [
[
'id' => 264454000038134081, # <-- Javascript won't handle this number //
'name' => 'Test 1',
],
[
'id' => '264454000038134082', # <-- It is good! //
'name' => 'Test 2',
],
];

As workaround, define the id as a string and use values-as-string attribute instead.

<x-choices ... values-as-string />
<x-choices-offline ... values-as-string />

Events

You can catch component events just like described on Livewire docs. In this case the component will trigger the @change-selection and it will contain the selected items keys.

The payload contains a single key or an array of keys, depending on how you set the component. Because it is a custom event, you must access the key(s) via the detail.value property on the event.

<x-choices ... @change-selection="console.log($event.detail.value)" />
<x-choices-offline ... @change-selection="console.log($event.detail.value)" />

Made with by Robson TenĂ³rio and contributors.