How to use form_for in liveview component without model or changeset

I’m trying to build a form with a select in a modal component, and I just need to retrieve the value selected. I wanted to use the form_for helper but cannot figure out how to do it without a model or a changeset. Is it possible?

1 Like

Welcome @baezanat - yes, you can use form_for/3 from within a component but without a changeset.

For example, you can use an atom in place of the changeset:

  <%= f = form_for :survey, "#", id: @id, phx_change: "validate", phx_target: @myself %>
    <!-- inputs go here -->
  </form>

and the the handle_event callback will get the form params like so:

def handle_event("validate", %{"survey" => params}, socket) do
  # do something with params
  {:noreply, socket}
end

You will notice that the key "survey" is the string version of the atom we passed to form_for.

7 Likes

Also, it’s important to note that to ensure the select input keeps its selected value, you need to track the choice in your assigns. The following is a bit longer example that I hope illustrates this point:

defmodule MyAppWeb.SelectExample do
  use MyAppWeb, :live_component

  @impl true
  def mount(socket) do
    {:ok,
     socket
     |> assign(:choices, ["A", "B", "C"])
     |> assign(:selected_choice, "")}
  end

  @impl true
  def handle_event("validate", %{"survey" => params}, socket) do
    case params["choice"] do
      "" ->
        {:noreply, socket}

      choice ->
        {:noreply, socket |> assign(:selected_choice, choice)}
    end
  end
end

To render the select input, you can use select/4:

<%= label(f, :choice) %>
<%= select(f, :choice, @choices, selected: @selected_choice) %>

or if you would prefer to build the select by hand, use options_for_select/2:

<select name="<%= input_name(f, :choice) %>" required>
  <option value=""<%= if @selected_choice == "", do: ~S( selected) %>>Choose one</option>
  <%= options_for_select(@choices, @selected_choice) %>
</select>
8 Likes

Do note that the form inputs will need to have their value: option set manually since there isn’t a way to drive it from the base form input.

Thank you! This is exactly what i was looking for.

1 Like

So I’m not able to get this to work. What am I doing wrong? I wasn’t able to get your syntax to work.

No matter what it just keeps submitting the default way:
no function clause matching Index.handle_event("alert", %{"_csrf_token" => "1223-456"}, Phoenix.LiveView.Socket...

Example:


 <%= form_for :survey, "#", [phx_submit: "alert", employee_id: @current_employee.id, user_id: user.id], fn _f -> %>
....

def handle_event("alert", %{"survey" => params}, socket) do
...

It only allow a list of params inside square brackets, else error.
How can I make the params go through?