Manually triggered form submits before updating its values (issue begins with Liveview 0.20.4)

OK, this is my first question on the forum, so I hope I’m able to properly spell out the situation I’m running into.

Since Liveview 0.20.4 I am having issues manually triggering a form’s submit action. Specifically the issue is that the action is triggering before the form is updated with new values.

My code was working before that release. So I’m not sure if this is a bug or I’m just doing something silly.

NOTE: I was able to hack a “solution” that demonstrates the issue by just doing an async delay on setting trigger_submit (this is not meant as a production ready solution, but rather it illustrates that delaying the submit somehow does allow for the form’s HTML to update).

Here is a redacted version (for simplicity) of the code in which the keys: user_id, vehicle and status arrive as empty strings on my POST controller.

  def render(assigns) do
    ~H"""
        <.simple_form
          for={@form}
          id="form"
          phx-trigger-action={@trigger_submit}
          action={~p"/vehicle?_action=insert"}
          method="post"
        >
          <!-- Form inputs - general -->
          <input type="hidden" name={@form[:user_id].name} value={@form[:user_id].value} />
          <input type="hidden" name={@form[:vehicle].name} value={@form[:vehicle].value} />
          <input type="hidden" name={@form[:status].name} value={@form[:status].value} />
       </.simple_form>
   """

  @doc """
  The submit chain of events is triggered by a user input on a live_component which calls this event.
  """
  def handle_info({:updated_inputs, {:time_zone_inputs, :checkout, component_inputs}}, socket) do
    current_params = socket.assigns.form.params
    updated_params = merge_state(component_inputs, current_params)
    handle_event("insert", %{"form" => updated_params}, socket)
  end

  def handle_event("insert", params, socket) do
    {:ok, final_params} = add_param_data(params["form"], socket)
    changeset = Vehicle.form_changeset(%Vehicle{}, final_params)
    if changeset.valid? do
        # HERE - this is the line were I've been able to locate the issue
        {:noreply, socket |> assign_form(changeset) |> assign(trigger_submit: true)}

        # To test my hypothesis I tried a hacky idea which does work.
        # the hack is to delay the trigger_submit
        # which suggests to me there is some issue with the form not identifying an update was needed on the HTML hidden inputs
        # socket =
        #   socket
        #   |> assign_form(changeset)
        #   |> start_async(:trigger_delay, fn -> :timer.sleep(700) end)
        # {:noreply, socket}
      end
    else
      {:noreply, socket |> put_flash(:error, "Data doesn't match the submission.")}
    end
  end

  # Handles the hack...
  # defp handle_async(:trigger_delay, {:ok, :ok}, socket) do
  #   {:noreply, socket |> assign(trigger_submit: true)}
  # end

  defp add_param_data(params, socket) do
    # receives a map of params (all keys are strings) and merges with data from assigns
    general_params = %{
      "user_id" => socket.assigns.current_user.id,
      "vehicle" => socket.assigns.vehicle,
      "status" => :pending,
    }
    {:ok, Map.merge(params, general_params)}
  end

  # same function that comes with Phoenix Accounts
  defp assign_form(socket, %Ecto.Changeset{} = changeset) do
    form = to_form(changeset, as: "form")
    if changeset.valid? do
      assign(socket, form: form, check_errors: false)
    else
      assign(socket, form: form)
    end
  end

I have tried to include the right amount of code to avoid overcomplicating the example and yet give a good sense of what I’m trying to accomplish and the issue I’m running into… but I also realize I may be WAY to deep into my own issue and may not realize I am lacking on detail.

Huge thanks for any help!

I have built a working example that illustrates the issue in full:

The app should be ready to go after doing a mix deps.get and starting the server.

To see the working vs error behaviour just change the liveview version in mix.exs.

Acutally it seems the issue is solved in the current main branch… although not yet released.

# using this in mix.exs gets a working liveview
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view", branch: "main", override: true},

I’m marking it as solved.

1 Like