Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hx-trigger="change" on a form does not react to input changes "outside" the form #2937

Open
ouvreboite opened this issue Oct 2, 2024 · 2 comments

Comments

@ouvreboite
Copy link

In HTML, it's possible to have form components (buttons, inputs, selects, ...) outside of a form by adding the form=#myFormId attribute on them.

<form id="myForm" hx-get...>
  <input type="text" name="text"/>
  <button type="submit">Submit</button>
</form>

<!-- I'm outside the form but still part of it -->
<select form="myForm" name="outside">
  <option>a</option>
  <option>b</option>
</select>

Using HTMX (via hx-boost or hx-get/hx-post) the outside select value is used correctly when making the call ✅ (ex: /currentPage?text=abc&outside=a)

But I can't find a way to trigger the form when the outside select value change.

<form id="myForm" hx-trigger="change" hx-get...>
  <input type="text" name="text"/> <!-- the form call will be triggered when this change -->
  <button type="submit">Submit</button>
</form>


<select form="myForm" name="outside" hx-trigger="change"> <!-- ... but not when this change -->
  <option>a</option>
  <option>b</option>
</select>

Example of the behavior: https://plnkr.co/edit/sXNvKYobVl6iD5NQ

A stopgap solution is to add a bit of JS on each out-of-form input: onchange="this.form.requestSubmit()" and have hx-trigger="submit change"

@MichaelWest22
Copy link
Contributor

Yeah the way form="xxx" and also hx-include work for adding remote inputs into a forms response do not propagate all of the various events from the remote objects back to the form. events like changed only bubble up to parents and this is just how events work in browsers sorry. Their are some exceptions like for buttons with form= set i think as these fire submit events to the remote form but this is all browser logic and not htmx.

I think the best solution is adding simple JS to the remote input to trigger the form which is what you have done. It is possible just with htmx to move the hx-get on the form up to a higher parent object to capture both the form and the selects change event and fire the request properly like this:

<body hx-get="item.html"
        hx-trigger="change"
        hx-target="#results"
        hx-swap="afterend"
        hx-include="#myForm">
    <section>
      <h1>Form</h1>
      <small>Selecting a value will trigger HTMX</small>
      <form
        id="myForm"
        "
      >
        <label>
          selectInForm
          <select name="selectInForm">
            <option>a</option>
            <option>b</option>
            <option>c</option>
          </select>
        </label>
      </form>
    </section>
    <section>
      <h1>Outside the form</h1>
      <small>HTMX is unable to react to change</small>
      <label>
        selectOutForm
        <select name="selectOutForm" form="myForm">
          <option>1</option>
          <option>2</option>
          <option>3</option>
        </select>
      </label>
    </section>
  </body>

This is not ideal on the body in this example but it shows how the events bubble up to parents and how you can use this sometimes.

You can also extend the attributes on the select to make it fire whatever event you like with htmx like this:

        <select name="selectOutForm" form="myForm" 
          hx-get="item.html"
          hx-trigger="change"
          hx-target="#results"
          hx-swap="afterend"
          hx-include="#myForm">
          <option>1</option>
          <option>2</option>
          <option>3</option>
        </select>

and then this select will function as its own customized htmx activated element. Also the hx-target, hx-swap and hx-include can all use inheritance so could be placed on a mutual parent element to reduce the attribute duplication if possible.

@Telroshan
Copy link
Collaborator

Telroshan commented Oct 3, 2024

Note that you can also use the from modifier of hx-trigger

For example, defining

<form id="myForm" hx-post="/whatever" hx-trigger="change, change from:[form='myForm']">
<input ...>
...
</form>
<input form="myForm" ...>

will catch the change event on the form itself (thus on any contained child), but also from any input that defines the attribute form="myForm"

Note that, as the doc mentions about from:

The CSS selector is only evaluated once and is not re-evaluated when the page changes. If you need to detect dynamically added elements use an event filter, for example click[event.target.matches('input')]

If you need a form that handles dynamically added external inputs, you could listen on the body instead and filter the triggering elements, something like

<form id="myForm" hx-post="/whatever" hx-trigger="change, change[event.target.matches('[form=\'myForm\']')] from:body">
<input ...>
...
</form>
<input form="myForm" ...>

You can try it out on this JSFiddle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants