htmx icon indicating copy to clipboard operation
htmx copied to clipboard

Different form serialisation behaviour depending on whether hx-ext='ws' is repeated in child nodes

Open aral opened this issue 1 year ago • 1 comments

Given the following Kitten code:

<div hx-ext='ws' ws-connect='/settings/curate.socket'>
  <form name='add' hx-ext='ws' ws-send>
    <label for='postUrl'>Post link:</label>
    <input
      id='postUrl'
      type='url'
      name='postUrl'
      placeholder='e.g., https://mastodon.ar.al/@aral/111572057761839773'
      required
    ></input>

    <button name='add' value='postUrl'>Add</button>
  </form>

  <details id='hint'>
    <summary>Help</summary>
    <p>Copy the post link from the context menu on the date of the post.</p>
    <img class='thumb' src='./copy-link.png' alt='Screenshot of Mastodon interface showing the context menu open on a post’s link with the Copy Link option showing.'>
  </details>

  <markdown>
    ## View and remove posts
  </markdown>

  <form name='remove' hx-ext='ws' ws-send>
    <ul id='posts'>
      ${posts.map(post => kitten.html`<${Post} post=${post} showRemoveButton />`)}
    </ul>
  </form>
</div>

Where the Post component looks something like this:

export default function Post ({ post, showRemoveButton = false }) {
  return kitten.html`
    <li class='Post component'>
      <${Avatar} post=${post} />
      <${Content} post=${post} />
      <if ${showRemoveButton}>
        <then>
          <button name='remove' value='${post.id}'>
            <${TrashIcon} title='Remove post by ${post.account.display_name}'/></button>
        </then>
      </if>
    </li>
    <style>
      .Post {
        display: flex; align-items: flex-start; column-gap: 1em; padding: 1em;
        margin-bottom: 1em; background-color: var(--background-alt); border-radius: 1em;
      }
    </style>
  `
}

Sending from the add form results in:

{
  add: 'postUrl',
  postUrl: 'https://foo.bar',
  HEADERS: {
    'HX-Request': 'true',
    'HX-Trigger': null,
    'HX-Trigger-Name': 'add',
    'HX-Target': null,
    'HX-Current-URL': 'https://localhost/settings'
  }
}

And from one of the remove buttons, e.g.,

{
  remove: '111806071511870339',
  HEADERS: {
    'HX-Request': 'true',
    'HX-Trigger': null,
    'HX-Trigger-Name': 'remove',
    'HX-Target': null,
    'HX-Current-URL': 'https://localhost/settings'
  }
}

Notice how the name and value on the button elements is serialised and included in the data.

However, if you remove the additional hx-ext='ws' declarations on the form elements, the results are:

{
  postUrl: 'https://foo.bar',
  HEADERS: {
    'HX-Request': 'true',
    'HX-Trigger': null,
    'HX-Trigger-Name': 'add',
    'HX-Target': null,
    'HX-Current-URL': 'https://localhost/settings'
  }
}

and

{
  HEADERS: {
    'HX-Request': 'true',
    'HX-Trigger': null,
    'HX-Trigger-Name': 'remove',
    'HX-Target': null,
    'HX-Current-URL': 'https://localhost/settings/'
  }
}

Notice the add and remove values, defined on the buttons, are missing.

I would expect it to always work like it does in the first case (serialising the name and values on buttons in forms also, as this is very useful in passing data when buttons are repeated, as seen here) but without having to repeat hx-ext='ws' on every child form.

Thoughts?

aral avatar Jan 25 '24 18:01 aral

This is the same issue as: https://github.com/bigskysoftware/htmx/discussions/970

aral avatar Jan 26 '24 21:01 aral