I want to build a web page that will have HTML form with a number of select components describing location: country, state, city and street. Choosing value in given select component will update options in 'downstream' selects.
I was able to implement it using HTML + Thymeleaf + Spring Boot + HTMX where htmx does hx-get, thymeleaf returns rendered fragments.
When whole page is generated (GET /test) select components do get correct name attribute names:
<span id="stateFragment">
<label for="stateId">State</label>
<select name="state" hx-get="test/cities" hx-target="#cityFragment" hx-swap="outerHTML" id="stateId" >
</select>
</span>
When requesting fragment (GET /test/states) th:field on selects, Thymeleaf removes name attribute value.:
<select name="" hx-get="test/cities" hx-target="#cityFragment" hx-swap="outerHTML" id="stateId">
<option value="ca_state1">ca_state1</option>
<option value="ca_state2">ca_state2</option>
</select>
HTML/Thymeleaf looks like below:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.thymeleaf.org" layout:decorate="~{layouts/defaultLayout}">
<head>
<title>Passing parameters</title>
</head>
<body>
<div layout:fragment="content" class="row g-6">
<div class="col">
<form th:action="@{/test/new}" th:object="${location}" method="post">
<label for="countryId">Country</label>
<select hx-get="test/states" hx-target="#stateFragment" hx-swap="outerHTML"
th:field="*{country}" id="countryId">
<option value="us">USA</option>
<option value="ca">Canada</option>
</select>
<span th:fragment="stateFragment" id="stateFragment">
<label for="stateId">State</label>
<select hx-get="test/cities" hx-target="#cityFragment" hx-swap="outerHTML"
th:field="*{state}" id="stateId">
<option th:each="state : ${states}" th:value="${state}" th:text="${state}"></option>
</select>
</span>
<span th:fragment="cityFragment" id="cityFragment">
<label for="cityId">City</label>
<select th:field="*{city}" id="cityId">
<option th:each="city : ${cities}" th:value="${city}" th:text="${city}"></option>
</select>
</span>
<button type="submit">Submit</button>
</form>
</div>
</div>
</body>
</html>
And the controller is:
@Controller
@RequestMapping("/test")
public class TestController {
@GetMapping
public String getLocation(Model model) {
model.addAttribute("location", new LocationDto());
model.addAttribute("states", List.of());
model.addAttribute("cities", List.of());
return "test/new";
}
@GetMapping("/states")
public String getStates(Model model, @RequestParam String country) {
model.addAttribute("state","");
model.addAttribute("states", List.of(country + "_state1", country + "_state2"));
return "test/new :: stateFragment";
}
@GetMapping("/cities")
public String getCities(Model model, @RequestParam String state) {
model.addAttribute("cities", List.of(state + "_city1", state + "_city2"));
return "test/new :: cityFragment";
}
}
How can I render fragment of page without losing name attribute value when rendering just a fragment?