Custom select field for form

I am working with forms, and i want to implement a custom select field. That is, a styled select with that displays and img tag (or a styled div) and the name of the selected option.

The liveview is the following:
lib/form_select_test_web/live/page_live.html.heex

<section class="prose">
  <%= f = form_for @changeset, "#", [phx_change: "update"], fn f -> %>
    <%= label(f, :assignee, class: "block text-sm font-medium text-gray-700") %>
    <%= select(f, :assignee, Enum.map(@users, & &1.name),
      class:
        "block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
    ) %>
  <% end %>
  <div>
    <.live_component
      module={CustomSelectComponent}
      id="sample-1"
      f={f}
      name={:assignee}
      options={@users}
    />
  </div>
</section>

It creates a form from a changeset and passes the form to a live component (the custom select).

lib/form_select_test_web/live/custom_select_component.ex

defmodule FormSelectTestWeb.Live.CustomSelectComponent do
  use FormSelectTestWeb, :live_component
  ...
  def update(assigns, socket) do
    %{f: f, name: name, options: options} = assigns

    value = "test" # how to get the selected value?
    selected_option = Enum.find(options, &(&1.name == value)) || List.first(options)

    {
      :ok,
      socket
      |> assign(assigns)
      |> assign(:selected_option, selected_option)
      |> assign(:name, name)
      |> assign(:f, f)
    }
  end

  def handle_event("toggle", _unsigned_params, socket) do
    {
      :noreply,
      socket
      |> assign(:expanded, !socket.assigns.expanded)
    }
  end
  ...
end

The problem i am having is how do i get the selected value from the form? I only get passed a {safe, form} value. But i want to access the form struct. What am i doing wrong?

For further references, i am using this post as inspiration:
https://fullstackphoenix.com/tutorials/how-to-create-a-custom-select-with-alpine-js-and-phoenix-liveview

Note: I am not using alpinejs, i want to implement it without the aplinejs part

Update:

If i move the livecomponet within the form tag

  <%= f = form_for @changeset, "#", [phx_change: "update"], fn f -> %>
    <%= label(f, :assignee, class: "block text-sm font-medium text-gray-700") %>
    <%= select(f, :assignee, Enum.map(@users, & &1.name),
      class:
        "block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
    ) %>
    <div>
      <.live_component
        module={CustomSelectComponent}
        id="sample-1"
        f={f}
        name={:assignee}
        options={@users}
        changeset={@changeset}
      />
    </div>
  <% end %>

i get the following error:

That tutorial is a little outdated now. I will rewrite it. I can see that you are using the older way of implementing forms. Is this a new project?

1 Like

Yes! This is a new project

I think the error suggests that you should use new-ish formtag

<.form for={@form} />
  and stuff
</.form>

That was the solution! I have now the form struct passed to the child live_component in which i can access the params.

Thanks for the help!

Is there a possibility to get news of when the tutorial is updated? :slight_smile: