Lab 3: Inline Editing

In this lab, we will create a group Management Page where we can add a user to a group and a navigation bar

Group Management

We start by creating a GroupManagementComponent ViewComponent in the de.tschuehly.easy.spring.auth.group.management package:

GroupManagementComponent.java
@ViewComponent
public class GroupManagementComponent {
  public static final String MODAL_CONTAINER_ID = "modalContainer";
  public static final String CLOSE_MODAL_EVENT = "close-modal";

  public record GroupManagementContext() implements ViewContext {  }

  public ViewContext render() {
    return new GroupManagementContext();
  }
}

The template is the same as the UserManagementComponent.jte but we added two <a> links to the <nav> element.

GroupManagementComponent.jte
@import static de.tschuehly.easy.spring.auth.group.management.GroupManagementComponent.CLOSE_MODAL_EVENT
@import static de.tschuehly.easy.spring.auth.group.management.GroupManagementComponent.MODAL_CONTAINER_ID
@import de.tschuehly.easy.spring.auth.group.management.GroupManagementComponent.GroupManagementContext
@param GroupManagementContext groupManagementContext
<html lang="en">

<head>
    <title>Easy Spring Auth</title>
    <link rel="stylesheet" href="/css/sakura.css" type="text/css">
    <script src="/htmx_1.9.11.js"></script>
    <script src="/htmx_debug.js"></script>
    <script src="http://localhost:35729/livereload.js"></script>
</head>
<body hx-ext="debug">
<nav>
    <h1>
        Easy Spring Auth
    </h1>
    <a href="/">UserManagement</a>
    <a href="/group-management">GroupManagement</a>
    <hr>
</nav>
<main>

</main>
</body>
<div id="${MODAL_CONTAINER_ID}" hx-on:$unsafe{CLOSE_MODAL_EVENT}="this.innerHTML = null">

</div>
</html>

Next, we will create a GroupTableComponent in the de.tschuehly.easy.spring.auth.group.management.table package.

We autowire the groupService and create a GROUP_TABLE_ID constant.

We add the corresponding template GroupTableComponent.jte and set the <table> id to ${GROUP_TABLE_ID}

Now back in the GroupTableComponent.java, we retrieve all groups with the groupService.getAll() method and add this List of groups to the ViewContext

Now we need to replace the <tbody> element of the GroupTableComponent.jte with the following:

(1): We loop over the groupTableContext.groupList() variable with the @for syntax

(2): We show the groupName in a <td>

(3): We loop over the group.memberList

(4): We show the username in a <span>

(5): If the group has no users we show a no member message


Then we render the GroupTableComponent in the GroupManagementComponent. We autowire it and pass it into the ViewContext

In the GroupManagementComponent.jte template we render the GroupTableComponent it in the <main> element, by using the viewContext

Now we need to add the /group-management endpoint to the GroupController:

(1): We autowire the GroupManagementComponent

(2): We create a new @GetMapping

(3): We return the ViewContext retrieved by calling the render() method

If we now run Lab3Application.java and navigate to localhost:8080/group-managementarrow-up-right to see the rendered groups and members.

circle-check

We haven't done anything new yet, now we are going to start with the inline editing feature.

Inline Editing

We now want to add a new User to one of the groups.

We autowire the GroupTableComponent and create an endpoint in the GroupController:

(1): We define a POST_ADD_USER constant

(2): We define a USER_ID_PARAM constant

(3): We capture the groupName via @PathVariable

(4): We capture the userId via @RequestParam

(5): We add the user to the group via the groupService

(6): We return the GroupTableComponent.render which will render the group table with the new user added

Next, we create a AddUserComponent in the de.tschuehly.easy.spring.auth.group.management.table.user package:

(1): We autowire the UserService

(2): The render method has a groupName parameter

(3): We pass the groupName and the userService.findAll to the AddUserContext.


Now we create a AddUserComponent.jte template in the same package as the AddUserComponent.java

(1): We create a <form> element add an hx-post attribute that targets the POST_ADD_USER endpoint and inserts the groupName in the ViewContext into the Endpoint URI using the HtmxUtil

(2): We target the GROUP_TABLE_ID and swap the outerHTML of the target element.

(3): We create a <select> and use the @for loop syntax to create an option element for each user.

circle-info

As you can see in contrast to the rerender method of the UserRowComponent here the htmx logic is in the template.


We now autowire the AddUserComponent and create a GET_SELECT_USER endpoint in the GroupController

Back to the GroupTableComponent.jte we add a static import to GET_SELECT_USER and HtmxUtiland add a new <td> in the @for loop.

(1): We create a <button> element that has a hx-get attribute that creates a GET request to /group/groupName/select-user

(2): We swap the outerHTML of the target element. As we didn't set the hx-targetwe replace the <button> element.

Now restart the application and navigate to localhost:8080/group-managementarrow-up-right.

We can click on the plus and see the selector to add a User to the group. When clicking on Add User to group the table is rerendered with the updated value.

circle-check

Last updated