Lab 5: Lazy Loading

In this lab, we simulate a slow system and we want to improve the user experience by giving them faster feedback.

Simulate a large payload and slow system

We start by creating a lot of users in the initializeUsers method in the Lab5Application class with the datafaker.net library.

Lab5Application.java

@SpringBootApplication
public class Lab5Application {

  public static void main(String[] args) {
    SpringApplication.run(Lab5Application.class, args);
  }
  @Bean
  public ApplicationRunner initializeUsers(UserService userService, GroupService groupService) {
    return (args) -> {
      EasyUser thomas = userService.createUser(
          "Thomas",
          "This is a password"
      );
      userService.createUser(
          "Cassandra",
          "Test1234"
      );
      groupService.createGroup("USER_GROUP");
      groupService.createGroup("ADMIN_GROUP");
      groupService.addUserToGroup("USER_GROUP", thomas.uuid);
      
      Faker faker = new Faker();
      for (int i = 0; i < 10000; i++) {
        userService.createUser(
                faker.internet().username(),
                faker.internet().password()
        );
      }
    };
  }
}

We also introduce a delay in the UserService.findAll method by adding a Thread.sleep to simulate a slow external system.

UserService.java
public List<EasyUser> findAll() {
  try {
    Thread.sleep(3000);
  } catch (InterruptedException e) {
    throw new RuntimeException(e);
  }
  return easyUserList;
} 

Problem

Now start the Lab5Application and navigate to http://localhost:8080.

You will see that the request will take 3 seconds suggesting an unresponsive system to the user.

To improve the user experience we want to lazy load the slow user table after the initial page load with htmx.

Lazy Loading the user table

We create a constant GET_USER_TABLE and then add an HTTP GET endpoint in the UserController where we render the userTableComponent .

UserController.java
public static final String GET_USER_TABLE = "/user-table";

@HxRequest // (1)
@GetMapping(GET_USER_TABLE)
public ViewContext userTable() {
  return userTableComponent.render();
}

(1): We use the @HxRequest annotation of the htmx-spring-boot library. The endpoint will only accept a request when it's made from htmx.

Now restart the Lab5Application and try to navigate to http://localhost:8080/user-table.

You should not be able to access it.

To render the user table we need to change the UserMangement back to a ViewComponent.

UserManagementComponent.java
@ViewComponent
@Order(1)
public class UserManagementComponent implements Page {

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

  public record UserManagementContext() implements ViewContext {}

  @Override
  public NavigationItem navigationItem() {
    return new NavigationItem("User Management", UserController.USER_MANAGEMENT_PATH);
  }
}

Now create a UserManagementComponent.jte file in the same package as the UserManagementComponent

@import static de.tschuehly.easy.spring.auth.user.UserController.GET_USER_TABLE
<div hx-get="${GET_USER_TABLE}" <%-- (1) --%>
     hx-trigger="load">  <%-- (2) --%>
    <img src="/spinner.svg" 
         class="htmx-indicator" <%-- (3) --%>
         alt="Result loading..." width="50" >
</div>

(1): The hx-get attribute creates a GET request to the GET_USER_TABLE endpoint.

(2): The hx-trigger="load" attribute triggers the request when the page loads.

We can show a loading spinner with an <img> element. (3): By adding the htmx-indicator CSS class the element is shown while the HTTP request of the parent htmx element is in flight.

Rendering the UserMangement

Now back in the UserController we autowire the userMangementComponent by injecting it into the constructor.

Then we replace the userTableComponent.render() method with userManagementComponent.render() in

UserController.java
public static final String USER_MANAGEMENT_PATH = "/";

@GetMapping(USER_MANAGEMENT_PATH)
public ViewContext userManagementComponent() {
  return layoutComponent.render(
      userManagementComponent.render()
  );
}

Restart the Lab5Application and navigate to http://localhost:8080.

You can see that the page loads instantly and only the user table rendering is delayed.

Lab-5 Checkpoint 1

If you are stuck you can resume at this checkpoint with:

git checkout tags/lab-5-checkpoint-1 -b lab-5-c1

Last updated