Search code examples
javaspring-mvcthymeleaf

How to transfer data to controller as hash map from thymeleaf form


Please help me to solve this problem: I have no idea how to transfer data from thymeleaf view to controller, that expected data as a hash map? So let me explain in more detail.

I have next pojo, that is used as a wrapper for my hashMap data. It looks like this:

public class DTDays {
  private Map<Driver, String> driversDays = new HashMap<>();

  public Map<Driver, String> getDriversDays() {
      return driversDays;
  }

  public void setDriversDays(Map<Driver, String> driversDays) {
      this.driversDays = driversDays;
  }
}

Controller has method with parameter model attribute and another pojo:

@RequestMapping(value = "/go", method = RequestMethod.POST)
public String advanced(@ModelAttribute DTDays dtDays,
                       @RequestParam(name = "tourId") Long tourId, Model model){
    // make some business logic with provided data 
    return "redirect:/tours/advanced";
}

Here I debugged and dtDays is not null, but map property is empty, also tourId is worked as expected, I can get right value.

Now to the problem, view:

<body>
<div style="margin-left: 20px;">
    <h1 th:text="${tour.tittle}"></h1>
    <p>Add tour interval for drivers</p>
    <form id="driverIntervals" th:action="@{/advanced/go}" th:object="${driversDays}" method="post">
        <table>
            <tr>
                <td>Drivers:</td>
                <td>Date:</td>
            </tr>
            <tr th:each="d: ${attachedDrivers}">
                <td th:text="${d.id+' '+d.fullName}" >
                    <input type="hidden" th:value="${d.id}" >
                </td>
                <td>
                    <input type="text" placeholder="Pick days" th:name="days"/>
                </td>
            </tr>
        </table>
        <input type="hidden" th:name="tourId" th:value="${tour.id}"/>
        <button type="submit">Confirm</button>
    </form>
</div>
</body>

View looks like this:

view

What should i write in view to submit data? In my case Driver is a key of the map and user entered data in related input field will be a value of a map.

I already know how to submit List, by using select-option in a view:

<select th:name="drivers2attach" multiple="multiple" id="attachDrivers">
    <!--/*@thymesVar id="drivers" type="java.util.List<stanislav.tun.novinomad.picasso.persistance.pojos.Driver>"*/-->
    <option th:each="d : ${drivers}" th:value="${d.id}"
            th:text="${d.getId()+' '+d.fullName}">
    </option>
</select>

and @RequestParam List list in controller:

@RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView addTourAction(@ModelAttribute("tour") Tour tour,
@RequestParam(required = false, name = "drivers2attach") List<Long> drivers2attach) 

But how to deal with map?

In case with list data is auto populated. In map only keys is prepopulated, this is count of drivers, and now i expect user input for each driver as a key value.

In researching of answers i already read these sources: How to bind an object list with thymeleaf?

Send list object from thymeleaf to controller

How do I load HashMap and ModelandView object values using Thymeleaf in a Spring Boot application?

Thymeleaf Map Form Binding

http://forum.thymeleaf.org/How-to-use-Map-lt-String-String-gt-with-Spring-and-Thymeleaf-forms-td4028666.html

http://forum.thymeleaf.org/How-to-use-method-POST-with-a-complex-Hashmap-lt-Object-list-lt-Object-gt-gt-td4031257.html

Thymeleaf Map Form Binding

Use HashMap as Form Backing Bean Spring MVC + ThymeLeaf etc.

But didn't helps. Somewhere in this links I found out that I should use some wrapper to do it, but again no idea why it not works, or what should I do additionally for make it working. Maybe I generally make wrong logic and to submit data as hashmap I shall convert data to list first and then somehow get map from it in controller?


Solution

  • Sorry for creating duplicate question, finally i found solution by following this answer on stackoverflow

    i wrote little project for test it and it works. So i was on right way in controller with a wrapper. I missed only view map representation and syntax;

    Finally view will looks like this: view

    here is view source code:

    <form th:action="@{/save}" th:object="${form}" method="post">
    <h1>Welcome</h1>
    <div th:each="property : ${form.properties.entrySet()}">
        <div class="form-group">
            <label th:for="*{properties['__${property.key}__']}" th:text="${property.key}">Property</label>
            <input type="text" class="form-control" th:field="*{properties['__${property.key}__']}" />
        </div>
    </div>
    <button type="submit">send to controller</button>
    

    and controller:

     @PostMapping("/save")
    public ModelAndView save(@ModelAttribute(name = "form") MapWrapper form){
        var mav = new ModelAndView();
        mav.setViewName("index");
        mav.addObject("mapWrapper", form);
        var map = form.getProperties();
    
        System.out.println("Size of map = "+map.size());
        for (Long id : map.keySet()) {
            System.out.println("Key: "+id+"; Value: "+map.get(id));
        }
    
        return mav;
    }
    

    and output is : output

    P.S. reason of asking here is that i stuck on this problem for about 2 weeks, I despaired, but found solution after create a question.