Lab 2: Using Spring ViewComponent

This lab aims to build the same application as Lab 1. But this time we will use Spring ViewComponent and htmx-spring-boot to delegate rendering responsibility to the ViewComponent.

An Introduction to Spring ViewComponent

A ViewComponent is a Spring-managed bean that defines a rendering context for a corresponding template, this context is called ViewContext.

We can create a ViewComponent by annotating a class with the @ViewComponent annotation and defining a public nested record that implements the ViewContext interface.

SimpleViewComponent.java
@ViewComponent
public class SimpleViewComponent {
    public record SimpleViewContext(String helloWorld) implements ViewContext {
    }

    public SimpleView render() {
        return new SimpleView("Hello World");
    }
}

A ViewComponent needs to have a template with the same name defined in the same package. In the template, we can access the properties of the record.

SimpleViewComponent.jte
@param SimpleViewComponent.SimpleViewContext simpleViewContext
<div>${simpleViewContext.helloWorld()}</div>

Spring ViewComponent wraps the underlying MVC model using Spring AOP and enables us to create the frontend in a similar way to the component-oriented JavaScript frameworks

Creating the UserMangement with Spring ViewComponent

To start we need to add three dependencies to the build.gradle.kts file.

We can enable live-reload for Spring ViewComponent with these properties in application.yaml.

Also, remove the gg.jte properties and uncomment the spring.view-component properties

We start by creating a UserManagementComponent.java file in the de.tschuehly.easy.spring.auth.user.management package.

We then create a UserManagementComponent.jte template in the de.tschuehly.easy.spring.auth.user.management package:

We add a static import to the UserManagementComponent class. The param is a UserManagementContext as we put all variables into this record.

Now we will create a separate component for the table.

UserTableComponent

Create a UserTableComponent.java in de.tschuehly.easy.spring.auth.user.management.table

In the last lab, we defined the USER_TABLE_BODY_ID in the UserController.java. Now define it in the UserTableComponent.java .

Now we will create a UserManagementComponent.jte template in the same package as the UserTableComponent.java:

We then create a UserRowComponent.java class in de.tschuehly.easy.spring.auth.user.management.table.row package:

We then create a UserRowComponent.jte template in the auth.user.management.table.row package.

(1): We use the userRowContext.easyUser() attribute to access the user we want to render.

(2): We set the id of the table row using the UserRowContext.htmlUserId(uuid) function

UserTableComponent

We now change the UserTableComponent.java:

(1): We autowire the userRowComponent.

(2): We add a List<ViewContext> property to the UserTableContext

(3): We call the userService.findAll() method

(4): Then we call the autowired userRowComponent::render method in the .stream().map() function.

Now we will render the userTableRowList in the UserTableComponent.jte:

(1): We loop through the userTableRowList and create a row loop variable.

(2): We render the row ViewContext in the <tbody>

Now we can render the table using Spring ViewComponent in the UserMangement.java:

(1): We autowire the UserTableComponent

(2): We add a userTable ViewContext field to the UserManagementContext

(3): In the UserManagementComponent.render method we call the userTableComponent.render and pass it into the UserManagementContext constructor.

In the UserManagementComponent.jte template, we can insert the rendered table:

In the UserController.java we can autowire the UserManagementComponent ViewComponent and render it in the index method:

We can restart the application, navigate to http://localhost:8080/ and see the table rendered.

Edit User

We now create the edit user functionality with Spring ViewComponent.

We create an EditUserComponent.java in the de.tschuehly.easy.spring.auth.user.management.edit package:

(1): We first autowire the userService in the constructor

(2): Then we create a render method with a uuid parameter.

(3): We get the user with the userService.findById(uuid) method

(4): We add the uuid, username and password of the user to the EditUserContext ViewContext


We then create the EditUserComponent.jte template in the same package as the EditUserComponent.java

In the UserController.java we remove the UserForm record, autowire the EditUserComponent and then change the editUserModal method to call the editUserComponent.render method.

We can restart the application navigate to http://localhost:8080/ and the edit user modal works again.

Fix the Save User functionality

In Lab1 we used HX Response headers to set the swapping functionality directly in the UserController.java:

We now want to move this functionality to the UserRowComponent.

HtmxUtil

I have already created a HtmxUtil class in the de.tschuehly.easy.spring.auth.htmx package that helps us set the HX Response Headers.

We are using Wim Deblauwes htmx-spring-boot library: github.com/wimdeblauwe/htmx-spring-boot. It offers a HtmxResponseHeader enum with all possible values and a HxSwapType enum.

We will add these convenience methods to the HtmxUtil.java class:

Back to the UserRowComponent we create a rerender function where we use these utility functions:

(1): We retarget to the id of the <tr> element we created with the UserRowContext.htmlUserId() function.

(2): We swap the outerHTML of the target element

(3). And we trigger the CLOSE_MODAL_EVENT

(4): Finally, we return the UserRowContext with the easyUser


In the UserController.saveUser method we can call the userRowComponent.rerender method

We can restart the application and navigate to http://localhost:8080/ and the save user function works again!

We have the advantage that the Controller doesn't need to know how the UserRowComponent template looks and what needs to be swapped. The UserRowComponent offers an API to rerender a row.

Create User

Finally, we need to migrate the create user functionality to Spring ViewComponent. We start by creating a CreateUserComponent in the de.tschuehly.easy.spring.auth.user.management.create package:

We now need to create a CreateUserComponent.jte in the same package as the CreateUserComponent.java

We can now call the createUserComponent.render method in the UserController.getCreateUserModal method:

We can restart the application and navigate to http://localhost:8080/ and the create user modal is shown when we click on Create User

Finally, we need to migrate the UserController.createUser method. Currently, it looks like this:

As before we want to move this code into the UserRowComponent.java, by creating a new renderNewRow function:

We can now simplify the UserController.createUser function:

Now if we restart the application we can save a new user and they are inserted at the start of the table.

Last updated