ketrab321
ketrab321

Reputation: 591

How to pass object from th:each loop to controller

I have form and corresponding controller to which list of objects is passed. After that this list is redirected as an attribute to another method and displayed in a website. After that I would like to pass object from this list via form in a carSearchResult.html file to controller. I was trying to do like it is described here Spring and Thymeleaf: Sending an object to a controller from a th:each table but it does not work properly. It is sending an object to controller but with fields property and model set as blank (but they are not null).

searchCar form

<form action="#" th:action="@{/user/searchCar}" method="post">
    <input type="radio" name="type" value="hatch" /> Hatchback <br/>
    <input type="radio" name="type" value="sedan" /> Sedan <br/>
    <input type="radio" name="type" value="combi" /> Kombi <br/>
    <input type="radio" name="type" value="sport" /> Sport <br/>
    <input type="radio" name="type" value="cabrio" /> Cabrio <br/>
    <input type="text" name="dailyPrice" placeholder="Price" /> <br/>
    <input type="date" name="dateOfRent" placeholder="Date of rent" /> <br/>
    <input type="date" name="dateOfReturn" placeholder="Date of return" /> <br/>
    <input type="submit" value="Show" />
</form>

Controller method for searchCar post request

@PostMapping("/user/searchCar")
public String searchCar(Model model, @RequestParam String type, @RequestParam double dailyPrice,
        @RequestParam Date dateOfRent, @RequestParam Date dateOfReturn, RedirectAttributes redirectAttr) {
    List<Car> allCarsList = carDao.getAllCars(type, dailyPrice, dateOfRent, dateOfReturn);
    redirectAttr.addFlashAttribute("carList", allCarsList);
    return "redirect:/user/carSearchResults";
}

Method where list is redirected

@GetMapping("/user/carSearchResults")
public String showCarSearchResults(Model model) {
    model.addAttribute("car", new Car());
    return "user/carSearchResults";
}

Form where list is displayed

<div th:each="car: ${carList}">
    <h3 th:text="${car.producer}"></h3>
    <h3 th:text="${car.model}"></h3>
    <form action="#" th:action="@{/user/showCarDetails}" method="post" th:object="${car}">
        <input type="hidden" th:field="*{producer}" /> <br/>            
        <input type="hidden" th:field="*{model}" /> <br/>
        <input type="submit" value="Show" />
    </form>
</div> 

Method to which I want to send object from th:each loop

@PostMapping("/user/showCarDetails")
public String showCarDetails(@ModelAttribute Car car) {
    System.out.println(car);
    return "user/showCarDetails";
}

After printing information about car in console I receive object below. It looks like it is passed but with parameters as a blank space. Can anyone help me? Many thanks.

Car [id=null, producer=, model=, seatsNumber=null, type=null, registrationNumber=null, dailyPrice=null, description=null]

EDIT

I have changed the form. Option with th:attr and th:value works but I do not really understand why th:field does not work and what is the difference between them. As I understand from documentation th:value is some kind of older version of th:field?

<div th:each="car: ${carList}" th:object="${car}">
    <h3 th:text="*{producer}"></h3>
    <h3 th:text="*{model}"></h3>
    <form action="#" th:action="@{/user/showCarDetails}" method="post">
        <input type="hidden" th:value="*{producer}" name="producer" /> <br/>       <!-- It works -->
        <input type="hidden" th:attr="value=*{producer}" name="producer" /> <br/>  <!-- It works -->        
        <input type="hidden" th:field="*{producer}" />                             <!-- It does not work -->
        <input type="submit" value="Pokaż" />
    </form>
</div> 

Upvotes: 2

Views: 5309

Answers (1)

Metroids
Metroids

Reputation: 20477

th:object works differently when you use it on a <form>, vs. when you use it other places. When you use it in a form, it expects it to resolve to a single variable on the model. You cannot use it with a temporary variable (like {$car} -- which isn't on the model, it's just a temporary variable created byth:each) because the attribute just isn't built to work like that.

th:field generates both the value and name tags -- but again, it has to work within the same limitations as th:object. The reason manually adding name and value attributes work, is because they happen to line up with what spring is expecting in a post and it's able to create the object based on the names.

As for how to solve the original problem... I think you have two options:

  1. Create a command object which has a List of Cars as a property. That way you can use the th:object and th:field properties. You'll have to have the submit button to supply which car is being edited.

  2. Continue manually creating the name and value fields like you already are.

Upvotes: 1

Related Questions