Build a custom select box and connect it properly to Live View

Hi there!

I’m trying to build a custom select box, but I don’t know how to connect it properly to Live View.

I am using Alpine.js to keep track of the open state of the dropdown/select box, along with a state for the selected item.

Without adding much styling, the first draft looks something like this:

<.form for={@form} phx-change="validate" phx-submit="submit" phx-target={@myself}>
  <div
    x-data="{ open: false, selectedLabel: '', selectedKey: '' }"
    @click.away="open = false"
    class="relative"
  >
    <input type="hidden" name="select" x-model="selectedKey" />
    <!-- Button -->
    <button @click="open = !open" type="button">
      <span
        class="max-w-[120px] overflow-hidden"
        x-text="selectedLabel === '' ? 'Select an option' : selectedLabel"
      >
      </span>
    </button>
    <!-- Menu -->
    <div x-show="open" class="absolute z-10 mt-2 rounded border bg-white" x-cloak>
      <ul class="max-h-[140px] overflow-auto [&>li]:cursor-pointer [&>li]:px-4 [&>li]:py-2 [&>li]:text-gray-500 hover:[&>li]:bg-gray-100">
        <li
          :for={{label, key} <- @options}
          @click={"selectedLabel = '#{label}'; selectedKey = '#{key}'; open = false;"}
          value={key}
        >
          <%= label %>
        </li>
      </ul>
    </div>
  </div>
</.form>

I have tried to use a hidden input with Alpine’s x-model attribute to copy the selected value from the list to a real form element. When I submit the form, the select value from the hidden input is available in the form parameters, but even though the value of the hidden input changes when an element is selected, the validate event never fires.

Also, I think there must be a better way to build a custom select than copying the alpine state into a hidden input and managing state at multiple places…

Do you guys have any (simple) examples of how to build a custom select box? Btw, my goal is to add search to it.

Any advice would be appreciated!

Give live_select a look to see if it fits your needs.

On a project I’m working on, I ended up using this js library and using push_event to send data from/to the client.