Search code examples
spring-mvcspring-bootthymeleaf

Populating drop-down when using spring-boot-starter-web and thymeleaf


I’m trying to create a web application with spring-boot-starter-web and thymeleaf. As a starting point, I’m using Spring’s Validating Form Input – Getting Started Guide (https://spring.io/guides/gs/validating-form-input/) because “these guides are designed to get you productive as quickly as possible – using the latest Spring project releases and techniques as recommended by the Spring team.”

I’ve changed the age input field to an age drop-down which is populated from a service. My modified code is below:

package hello;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Controller
public class WebController implements WebMvcConfigurer {
    @Autowired
    AgeService ageService;
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/results").setViewName("results");
    }
    @GetMapping("/")
    public String showForm(Model model) {
        PersonForm personForm = new PersonForm();
        model.addAttribute("personForm", personForm);
        List<Integer> validAges = ageService.getValidAges();
        model.addAttribute("validAges", validAges);
        return "form";
    }
    @PostMapping("/")
    public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "form";
        }
        return "redirect:/results";
    }
}

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<form action="#" th:action="@{/}" th:object="${personForm}" method="post">
    <table>
        <tr>
            <td>Name:</td>
            <td><input type="text" th:field="*{name}"/></td>
            <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
        </tr>
        <tr>
            <td>Age:</td>
            <td><select th:field="*{age}">
                <option value="">Please select ...</option>
                <option th:each="validAge:${validAges}" th:value="${validAge}" th:text="${validAge}"></option>
            </select></td>
            <td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
        </tr>
        <tr>
            <td>
                <button type="submit">Submit</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

When the form is first shown everything is fine, but if there is a validation error and the form is redisplayed, then the drop-down only has the “Please select .. “ option.

What is the recommended technique to make this work?


Solution

  • The drop down was not populated because after bindingResult object finds errors, before returning the form, it seems a list of validAges was not added to the model as an attribute.

    Please add:

    Model model, as parameter to your checkPersonInfo as shown

       CheckPersonInfo(Model model, ...) 
    

    Add validaAges to your model in bindingResult.hasErrors block as shown

    if(bindingResult.hasErrors()){
       model.addAttribute("validAges", ageService.getValidAges());
       return "form";
    }