Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

Custom select options field in a blade component with AlpineJS

Hello,

I'm trying to write my own custom select options field in a blade component with AlpineJS.

I have an error : items.find is not a function. It comes from items: '{{ $items }}' but I don't know what to do else (@json($items) also gives me an error).

Do you have any idea how to solve this error ?

Beyond the error, how can I improve this code ? I'm sure it can probably be better. Perhaps by using Livewire + AlpineJS ?

Thanks for your help.

V

@props([
    'label',
    'field',
    'optionLabel' => 'name',
    'optionId' => 'id',
    'model',
    'nullOptionLabel',
    'items',
])

<div
	x-data="{
        show: false,
        items: '{{ $items }}',
        selectedOptionId: '{{ $model->$field }}',
        selectedOptionLabel: '',
        selectOption(id, label) {
            this.selectedOptionId = id;
            this.selectedOptionLabel = label;
            this.show = false;
        }
    }"
    x-init="
        selectedOptionLabel = selectedOptionId > 0 ? items.find(item => item.id == selectedOptionId).name : '{{ $nullOptionLabel }}';
    "
    x-on:click.outside="show = false"
    class="flex flex-col gap-1 select-none"
>
    <label class="text-gray-500" for="{{ $field }}">{{ $label }}</label>

    <div class="relative cursor-pointer">
        <input type="hidden" :value="selectedOptionId">

        <div x-on:click="show = !show" class="flex justify-between items-center px-2 py-1 bg-gray-100 border border-x border-gray-900 rounded-t" :class="{ 'rounded-b': !show }">
            <span x-text="selectedOptionLabel"></span>
            <span class="text-sm"><i class="fa-solid fa-chevron-down"></i></span>
        </div>

        <template x-if="true">
            <ul x-transition x-show="show" class="absolute w-full z-10 bg-gray-100 border-x border-b border-gray-900 rounded-b max-h-48 overflow-y-auto">
                @foreach ($items as $item)
                    <li x-on:click="selectOption({{ $item->$optionId }}, '{{ $item->$optionLabel }}')" class="px-2 py-1 last:rounded-b hover:bg-green-200">{{ $item->$optionLabel }}</li>
                @endforeach
            </ul>
    </template>
</div>
0 likes
2 replies
Pippo's avatar

You don't explain how $items is structured, anyway I think you can use this form in x-data:

items: {{ Js::from($items) }}

and you can add in @props a default value for items

'items' => []

Moreover, to use items.find you have to ensure that props['items'] contains an array or a collection

1 like
vincent15000's avatar

@Pippo Sorry, items are just an array of objects containing an id and a name.

How would you improve this code ? I mean ... would it be better to do this only with AlpineJS ? only with Livewire ? perhaps with both AlpineJS and Livewire ?

I realize that with only AlpineJS it's a bit complicated, it would probably be more easy and clear with Livewire.

But on the other hand it's only a field which interacts with the user on the frontend, so what would be the benefit to use Livewire which calls the backend to refresh the page ?

Please or to participate in this conversation.