MeMeto3
MeMeto3

Reputation: 13

Swapping in elements in a table with HTMX

I've been trying to use HTMX to append a new row to the table I'm displaying. Im using a hx-swap-oob attribute so I can redraw the form and swap inside the table the new row. The problem is that I'm getting everything inserted where the form is, so the form is correct, but the new row appears, without <tr> label below the form.

Here is the HTML code:

Main template:

{{ define "allUsers" }}
<body>
    {{ template "form" .Form }}
    {{ template "contacts" .Data }}
</body>
{{ end }}

Form:

{{ define "form" }}
<form hx-swap="outerHTML" hx-post="/user" id="create-user-form">
    <div id="form-fields">
        Name: <input {{ if .Values.name }} value="{{ .Values.name }}" {{ end }} type="text" name="name">
        Email: <input {{ if .Values.email }} value="{{ .Values.email }}" {{ end }} type="text" name="email">
        Password: <input {{ if .Values.password }} value="{{ .Values.password }}" {{ end }} type="text" name="password">
        {{ if .Errors }}
        <div id="error-msg">{{ .Errors.msg }}</div>
        {{end}}
    </div>
    <button id="create-user-button" type="submit">CreateUser</button>
</form>
{{ end }}

Contacts:

{{ define "contacts" }}
<div id="display">
    <div id="container-h1">
        <h1>Users</h1>
    </div>
    <div id="container">
        <table id="contacts">
            <thead>
                <tr>
                    <th id="uuid-column">ID</th>
                    <th>Name</th>
                    <th>Age</th>
                    <th>Email</th>
                    <th> - </th>
                </tr>
            </thead>
            <tbody id="contacts-body">
                {{ range .Contacts }}
                {{ template "contact" . }}
                {{ end }}
            </tbody>
        </table>
    </div>
</div>
{{ end }}

This is what im trying to render with the htmx-swap-oob:

{{ define "contact" }}
<tr id="contact-{{ .ID }}">
    <td id="uuid-column">{{ .ID }}</td>
    <td><strong>{{ .Name }}</strong> </td>
    <td>{{ .Age }} </td>
    <td>{{ .Email }}</td>
    <td id="button-cell">
        <button id="edit-button">
            <i class="fa-solid fa-edit" id="edit-icon"></i>
        </button>
        <button id="delete-button">
            <i class="fa-solid fa-trash" id="delete-icon" hx-delete="/delete/{{ .ID }}" hx-swap="outerHTML"
                hx-target="#contact-{{ .ID }}"></i>
        </button>
    </td>
</tr>
{{ end }}

And here Im sending the data from the backend once the form is being submited:

{{ define "form-and-oob" }}
{{ template "form" .Form }}
<tr hx-swap-oob="afterbegin:#contacts-body">
    {{ template "contact" .Data.Contact }}
</tr>
{{ end }}

And here is my handler written in go, using echo:

func (s *ServerBU) handlePost(c echo.Context) error {
    name := c.FormValue("name")
    email := c.FormValue("email")
    password := c.FormValue("password")

    if !s.EmailIndex.IsDuplicate(email) {
        log.Printf("Email %s already in use\n\n\n", email)

        form := NewForm()
        form.Values["name"] = name
        form.Values["email"] = email
        form.Values["password"] = password
        form.Errors["msg"] = "Email already registered."
        return c.Render(422, "form", form)

    }

    id, newUser, err := NewUser(name, "default", email, password, 42069)
    if err != nil {
        form := NewForm()
        form.Values["name"] = name
        form.Values["email"] = email
        form.Values["password"] = password
        form.Errors["msg"] = "Some error. Please try again."
        return c.Render(422, "form", form)
    }

    s.EmailIndex.Put(email, id.String())
    s.Storage.Put(id.String(), *newUser)

    p := NewPage(*NewForm(), Data{Contact: *newUser})

    return c.Render(200, "form-and-oob", p)
}

I tried to use a direct target to the <tbody> but I still get the same results. Also just sending the contact, without the form to be redrawn, but the new row still is being drawn under the form. I just cannot find the way to target the correct thing, it just does not move from the area below the form!

EDIT: I also have this script added to the body, I do not thing is where the problem is, but I put it because maybe that "htmx:load" does something that I am not expecting.

        document.addEventListener("htmx:load", function () {
            document.addEventListener("htmx:beforeSwap", function (evt) {
                if (evt.detail.xhr.status === 422) {
                    evt.detail.shouldSwap = true;
                    evt.detail.isError = false;
                }
            });
        });
    </script>

And here I have the generated HTML before the Post request:

<body>
    <form id="create-user-form" hx-swap="outerHTML" hx-post="/user">
        <div id="form-fields">
            Name:
            <input type="text" name="name" Email: <input type="text" name="email"> Password:
            <input type="text" name="password">
        </div>
        <button id="create-user-button" type="submit">CreateUser</button>
    </form>
    <div id="display">
        <div id="container-h1">
            <h1>Users</h1>
        </div>
        <div id="container">
            <table id="contacts">
                <thead></thead>
                <tbody id="contacts-body">
                    <tr id="contact-65925514-e6fd-11ef-b1a9-5811223c9280"></tr>
                    <tr id="contact-65b32fcd-e6fd-11ef-b1a9-5811223c9280"></tr>
                    <tr id="contact-65d35ccc-e6fd-11ef-b1a9-5811223c9280"></tr>
                    <tr id="contact-65f220af-e6fd-11ef-b1a9-5811223c9280"></tr>
                    <tr id="contact-6610cbd3-e6fd-11ef-b1a9-5811223c9280"></tr>
                </tbody>
            </table>
        </div>
    </div>

And after, i did not put the inside of the #display, but it is untouched:

<body>
    <form id="create-user-form" hx-swap="outerHTML" hx-post="/user"></form> 6a9cdee7-e719-11ef-9452-5811223c9280
    <strong class="">asd</strong>
    42069 qweasdasd
    <button id="edit-button">
    </button>
    <button id="delete-button"> </button>
    <div id="display"></div>

Upvotes: 1

Views: 48

Answers (1)

Dauros
Dauros

Reputation: 10517

As the documentation of hx-swap-oob explains, when you use a swap strategy other than outerHTML, the encapsulating tag pair will be striped. Therefore you need to embed the row element inside a real table.

{{ define "form-and-oob" }}
{{ template "form" .Form }}
<table>
  <tbody hx-swap-oob="afterbegin:#contacts-body">
    <tr>
      {{ template "contact" .Data.Contact }}
    </tr>
  </tbody>
</table>
{{ end }}

Upvotes: 0

Related Questions