How can I send an event when the value from <input type=number> changes?

Hi,

I have a LiveView with a simple_form.
If the user selects that he want a TV, then he can also define how many.

I have read the other posts regarding phx-change on input, but I could not find a solution that helped me.

I tried to add phx-change to the input, as you can see here.
The event is triggered, but the value specified in phx-value-company_id is not passed along with the params.
From the logs:

[debug] HANDLE EVENT “update_qty” in WizardLive.Summary.Edit
Component: Components.ComponentSelector
Parameters: %{“_target” => [“undefined”]}

I want this part of the form to be dynamic. The user should be able to specify the number of parts he wants.
I want to pass the ID of the component and the value defined in the input field, as arguments with the update_qty event.

            <input
              type="number"
              id={"qty-#{component.id}"}
              min="1"
              max="100"
              value="1"
              phx-target={@myself}
              phx-debounce="blur"
              phx-value-component_id={component.id}
              phx-hook="PersistFocus"
              phx-change="update_qty"
           />

  <!-- This works as expected: -->
            <div phx-click="inc" phx-value-myvar1="val1" phx-value-myvar2="val2">
           click
           </div>

It should work without issue

I used this form as a test and it passes the value 1 along with it on validation

    <.simple_form
     for={@form}
     id="hour-form"
     phx-target={@myself}
     phx-change="validate"
     phx-value-test={1}
     phx-submit="save"
   >

, “test” => “1”}

Your parameters say target undefined, are you sure you need phx-target={@myself}?
Or do you have anymore context?

Or how about using a hidden input field?

If it’s a list of items, wouldn’t this be a case for inputs_for?

Hi @Thr3x,

Here’s some more context.

It all starts here:

defmodule MyAppWeb.WizardLive.Summary.Edit do
  def render(assigns) do
    ~H"""
    <div class="page-container">
      <div class="form-container" phx-update="stream" id="wizardform">
        <.live_component
          module={MyAppWeb.WizardLive.Form}
          summary={@summary}
          id="form"
          summary_id={@summary.id}
          step={@step}
          sub_step={@sub_step}
        />
      </div>

Then we have WizardLive.Form:

  def render(assigns) do
    ~H"""
    <div id="wizard">
      <.simple_form
        for={@form}
        phx-change="validate"
        phx-submit="save"
        id={@id}
        class="w-full"
        phx-target={@myself}
      >

        <%= if @step == "select_speakers" do %>
          <.wizard_header text="Select Speakers" />

          <.live_component
            id="component_selector"
            module={ComponentSelector}
            selected_components={@selected_components}
            key={:speaker}
            name="Speaker"
            coming_from="speaker_selected"
            form={@form}
            selectable_options={Speakers.list_for_dropdown()}
          />

And here we have the last component, the ComponentSelector:


 def render(assigns) do

    ~H"""
    <div class="component-selector">
      <.input field={@form[:coming_from]} type="hidden" value={@coming_from} />
      <.input field={@form[@key]} type="hidden" value={"#{String.downcase(@name)}_selected" } />

      <select
        phx-change="component_selected_from_dropdown"
        name="component_selected"
        phx-target={@myself}
      >
        <%= options_for_select(@selectable_options, 0) %>
      </select>

      <button
        form="ignore-me"
        phx-click="add_component"
        phx-target={@myself}
        value={"#{String.downcase(@name)}" }
        class="ml-2 btn btn-primary"
      >
        Add
      </button>

      <.grid>
        <div :for={component <- @selected_components} class="grid-basic" phx-value-id={component.id}>
          <.small_card
            title={component.name}
            image_url={
              Map.has_key?(component, :notion_cover_image_url) && component.notion_cover_image_url
            }
            kind={:elevated}
            description={@description}
          >
            <button
              form="ignore-me"
              phx-click="remove_component"
              phx-target={@myself}
              value={component.id}
              class="trash-icon"
            >

            </button>

            <input
              type="number"
              id={"qty-#{component.id}"}
              min="1"
              max="100"
              value="1"
              phx-target={@myself}
              phx-debounce="blur"
              phx-value-company_id={component.id}
              phx-hook="PersistFocus"
              phx-change="update_qty"
           />

           <div phx-click="inc" phx-value-myvar1="val1" phx-value-myvar2="val2">

I can definitely use a hidden field.
If I can update the value of the hidden field when the value of the number input is changed, then perhaps that value can be passed along when I submit the form?

Do you have any suggestions on how I can design that hidden field to deliver the number value?

Looking at this:

doesn’t seem right. The value of the numeric field should be included. I think the problem might be that you don’t have a name attribute set on your input. This is why _target is undefined and the value of the field is not being sent.

1 Like

Thanks for suggesting the missing name field.
I added that, and now I get the qty in the params:

[debug] HANDLE EVENT “update_qty” in WizardLive.Summary.Edit
Component: Components.ComponentSelector
Parameters: %{“_target” => [“qty”], “qty” => “3”}

            <input
              type="number"
              name="qty"
              id={"qty-#{component.id}"}
              min="1"
              max="100"
              value="1"
              phx-target={@myself}
              phx-debounce="blur"
              phx-hook="PersistFocus"
              phx-change="update_qty"
           />

Now, I just need a way to deliver the component_id at the same time. :slight_smile:

Glad that helped.

The phx-value-*’ attributes won’t be included with a change event. The change event only includes form data.

I can think of a couple of options. You could set the name attribute to the id of the component, such as name={"qty_" <> component.id}. Then you’ll get the id in the params when the input changes: "qty_someid" => 2

Another option is to move the phx-change attribute to the form element. This will trigger the change event when any input changes but the whole form (including hidden fields, with the component id) will be included in the params. You already have this setup as validate so you could just handle the quantity field there.

I’d suggest you looking into inputs_for and nested forms. You’re half way on reimplementing what it does.

:+1: This is almost certainly the right way to go. (Didn’t mention it previously as I wanted to focus on answering the question)