Why do I see different params passed to validate and save from the same form?

Hi,

I’m building a LiveView .simple_form, but I’m not sure how to stitch everything together correctly.

I can see in the logs that every field in the form is passed to the “validate” event, but not to the “save” event.
I don’t understand why.
I also see that some of the “validate” events have a target “summary” and some have “matrix”. I would like all these fields to be grouped into the same namespace.

From the logs


[debug] HANDLE EVENT "validate" in EasyWeb.WizardLive.Summary.Edit
  Component: EasyWeb.WizardLive.Form
  Parameters: %{"_target" => ["summary", "nr_of_pip_sources"], 
"include_main_source" => "true", 
"summary" => %{"coming_from" => "solution_information", 
"id" => "10", "nr_of_pip_sources" => "22"}}

[debug] HANDLE EVENT "validate" in EasyWeb.WizardLive.Summary.Edit
  Component: EasyWeb.WizardLive.Form
  Parameters: %{"_target" => ["matrix"], "matrix" => "modular", 
"summary" => %{"coming_from" => "solution_information", 
"id" => "10", "nr_of_pip_sources" => "22"}}

[debug] HANDLE EVENT "save" in EasyWeb.WizardLive.Summary.Edit
  Component: EasyWeb.WizardLive.Form
  Parameters: %{"matrix" => "modular", 
"summary" => %{"coming_from" => "solution_information", 
"id" => "10", "nr_of_pip_sources" => "22"}}

form.html.heex

<div id="wizard">

  <.simple_form
    for={@form}
    phx-change="validate"
    phx-submit="save"
    id={@id}
    class="w-full"
    phx-target={@myself}
  >

    <%= if @step == "solution_information" and @sub_step == "details" do %>
      <h2 class=" mb-2 text-3lg font-extrabold tracking-tight text-gray-900 dark:text-white">
        Solution information X
      </h2>

      <.input field={@form[:id]} type="hidden" value={@summary_id} />
      <.input field={@form[:coming_from]} type="hidden" value="solution_information" />

      Do you need the system to include a main source? <br />

      <.input
        name="include_main_source"
        label="Yes"
        field={@form[:include_main_source]}
        id="yes"
        type="radio"
        value="true"
      />

      <.input
        id="nr_of_pip_sources"
        field={@form[:nr_of_pip_sources]}
        label="Number of sim PIP sources"
        type="number"
        phx-debounce="blur"
        phx-feedback-for="name"
      />

      <strong>What type of Matrix do you want to use?</strong>
      <.input
        id="fixed_standalone"
        field={@form[:matrix]}
        label="Fixed standalone"
        type="radio"
        name="matrix"
        value="fixed_standalone"
      />

      <.input
        label="Modular"
        id="modular"
        type="radio"
        name="matrix"
        field={@form[:matrix]}
        value="modular"
      />

      <.input
        label="AV Over IP"
        id="av_over_ip"
        type="radio"
        name="matrix"
        field={@form[:matrix]}
        value="av_over_ip"
      />
    <% end %>

    <:actions>
      <div class="mt-2 flex items-center justify-between gap-6">
        <.button phx-disable-with="Saving..." >
          Save
        </.button>
      </div>
    </:actions>
  </.simple_form>
</div>

What’s with the <:actions> section? Does that even generate valid html?

That’s a slot in the <.simple_form> component as generated by phoenix.

1 Like

I tried to remove the <:actions> tags.
Didn’t seem to make any difference.

include_main_source is part of the params that’s passed to the “validate” event,
but they are not sent to “save”.

Can you see why they are not?

What is the starting data of your @form object?

And are the 3 log events the result of interacting with the same form consecutively, or was the page refreshed in between in each one?

include_main_source is a radio button and if it’s not selected then HTML forms don’t include it in the params, same for “matrix”.

Since include_main_source is a single radio button, you may want a checkbox instead? Otherwise how do you deselect it?

When you set name="matrix", the value is decoded by Plug.Conn.Query.decode/4 as the map %{"matrix" => "modular"}. When you don’t set name directly, such as your nr_of_pip_sources input, then the input component uses the @form’s data to set a name and id.

So if you have a struct %Summary{nr_of_pip_sources: 0, ...}, the input will be rendered as <input name="summary[nr_of_pip_sources]" id="summary_nr_of_pip_sources" ...> so that decode/4 then creates %{"summary" => %{"nr_of_pip_sources" => 22}}

I managed to fix it by following Chris MacCord’s suggestion regarding radio buttons.

The complete page now looks like this:

<div id="wizard">

  <.simple_form
    for={@form}
    phx-change="validate"
    phx-submit="save"
    id={@id}
    class="w-full"
    phx-target={@myself}
  >

    <%= if @step == :room_information do %>
      <.input field={@form[:name]} label="Room name" phx-debounce="blur" phx-feedback-for="name" />
    <% end %>

    <%= if @step == "room_type" do %>
      <label for="room_type">Room Type</label>
      <select name="room_type">
        <%= options_for_select(Wizard.list_room_types(), 1) %>
      </select>
    <% end %>

    <%= if @step == "solution_information" and @sub_step == "check" do %>
      <.radio_group field={@form[:include_main_source]}>
        <:radio value="false">No</:radio>
        <:radio value="true">Yes</:radio>
      </.radio_group>

      <br />
      <.input
        field={@form[:nr_of_pip_sources]}
        label="Number of sources"
        type="number"
        phx-debounce="blur"
        phx-feedback-for="name"
      />
    <% end %>

    <%= if @step == "select_display" and @sub_step == "check" do %>
      <h2 class="text-lg pb-8"> Wall </h2>

      <.radio_group field={@form[:videowall_type]}>
        <:radio value="projection">Projection</:radio>
        <:radio value="LED">LED</:radio>
        <:radio value="LCD">LCD</:radio>
      </.radio_group>
    <% end %>

      <div class="mt-2 flex items-center justify-between gap-6">
        <.button
          class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
          phx-disable-with="Saving..."
        >
          Save
        </.button>
      </div>

  </.simple_form>
</div>
2 Likes