What does it do that a native html select cannot?
Jun 17, 2023
5
Level 63
How to improve this code of a custom select / options field ?
Hello,
It's the first time that I create a custom select / options field with Livewire / Blade / AlpineJS.
It works fine, but I'm sure that it can be improved.
Livewire controller
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Select extends Component
{
public $label;
public $nullOptionLabel;
public $items;
public $item_key;
public $fieldName;
public $class;
public function updatedItemKey()
{
$this->emitUp('itemKeyUpdated', ['field' => $this->fieldName, 'key' => $this->item_key]);
}
public function render()
{
return view('livewire.select');
}
}
Livewire view
<div>
<input type="hidden" name="{{ $fieldName }}" wire:model="item_key">
<x-form.select :class="$class" :label="$label" :nullOptionLabel="$nullOptionLabel" :items="$items" wire:model="item_key"></x-form.select>
</div>
Blade component
@props([
'label',
'nullOptionLabel',
'items',
])
<div
{{ $attributes->merge(['class' => 'relative flex flex-col gap-1 select-none']) }}
x-data="{
show: false,
label: '{{ $label }}',
nullOptionLabel: '{{ $nullOptionLabel }}',
selectedItemLabel: '{{ $nullOptionLabel }}',
items: {{ json_encode($items) }},
item_key: @entangle($attributes->wire('model')),
selectItem(itemKey) {
this.item_key = itemKey;
this.show = false;
}
}"
x-init="
if (item_key) {
const selectedItem = items.find(function (item) {
return item.key == item_key;
});
selectedItemLabel = selectedItem.value;
} else {
selectedItemLabel = nullOptionLabel;
}
$watch('item_key', function (value) {
if (value) {
const selectedItem = items.find(function (item) {
return item.key == value;
});
selectedItemLabel = selectedItem.value;
} else {
selectedItemLabel = nullOptionLabel;
}
});
"
@click.away="show=false"
>
<label class="text-gray-500" for="item_key" x-html="label"></label>
<div class="flex justify-between items-center px-3 py-1 border border-black rounded cursor-pointer" @click="show = !show">
<label class="cursor-pointer" x-html="selectedItemLabel"></label>
<span class="text-xs"><i class="fa-solid fa-chevron-down"></i></span>
</div>
<template x-if="true">
<ul class="z-20 absolute top-16 left-0 w-full max-h-60 overflow-y-auto bg-slate-800 border-2 border-slate-700 text-slate-300 rounded cursor-pointer" x-show="show" x-transition>
<li class="w-full px-3 py-1 first:rounded-t last:rounded-b hover:bg-lime-500 hover:text-black" :class="item_key == null ? 'text-lime-500' : ''" @click="selectItem(null)">{{ $nullOptionLabel }}</li>
@foreach($items as $item)
<li class="w-full px-3 py-1 first:rounded-t last:rounded-b hover:bg-lime-500 hover:text-black" :class="item_key == {{ $item['key'] }} ? 'text-lime-500' : ''" @click="selectItem({{ $item['key'] }})">{{ $item['value'] }}</li>
@endforeach
</ul>
</template>
</div>
And here how I use it in a view.
@livewire('select', [
'label' => 'Compte',
'nullOptionLabel' => 'Choisir un compte',
'items' => $accounts,
'item_key' => $transaction->account_id,
'fieldName' => 'account_id',
])
Thanks for your suggestions.
V
Please or to participate in this conversation.