Reputation: 31
A springboot-thymeleaf newbie, I have been reading many similar questions on this topic but I'm still missing something in the syntax and overall Springboot Thymeleaf paradigm.
The web application pulls project data from the backend database which is rendered by Thymeleaf templates. The user also can generate a pdf report of the data which is also rendered in the browser.
The entity:
@Entity
@Table(name = "REPORT_CS")
public class ReportItemCs {
public ReportItemCs () {}
@Id
@Column(name = "ITEM_ID")
private Long itemId;
@Column(name = "PROJ_NUM")
private String projectNumber;
@Column(name = "REGION")
private String region;
// additional fields, getters, setters
First, something that works:
A simple text field in thymeleaf is used to pass a string in post request. The controller picks it up where it's passed to the repo as a query parameter on projectNumber. The query returns a list of objects which thymeleaf renders in a table. Note there's no binding to any object - it's just posting the string from the UI then passing it as a query param.
Html:
//Works without binding to backend
<section layout:fragment="content">
<p>Quick Project Search:</p>
<form method="post">
<input type="text" name="keyword"
placeholder="Project number keywords" /> <input type="submit"
value="Search" />
</form>
<br /> <span th:text=" ${noProjectsMessage}"></span>
</section>
Controller:
@RequestMapping(value = "/", method = RequestMethod.POST)
public String showProject(String keyword, ModelMap model) {
List<ProjectView> p = repository.findByProjectsContaining(keyword);
if (p.size() == 0) {
model.put("noProjectsMessage",
String.format("Project with id containing \"%s\" not found...", keyword));
return "home";
} else {
model.put("projectViews", p);
}
return "show-projects";
}
Repository:
@Query("SELECT p FROM ProjectView p WHERE p.projectNumber like %?1%")
List<ProjectView> findByProjectsContaining(@Param("keyword") String keyword);
So, now, I need to add some checkboxes to provide additional filtering by region, project category, etc. I plan to use the checkboxes in two ways: 1) to dynamically filter the project list in the UI using jQuery and, also, to pass the checkbox values back to the controller so they can be used to populate a pdf template header. I'd then either do another database query or use Stream() to filter the list that was generated by the original query and send the filtered list to the pdf service. When the user clicks the "PDF" button, the checkbox values are forwarded to the pdf service where the report header and report are generated and returned as a byte stream in a separate tab.
Html
<div class="form-check">
<form th:action="@{/cs-report}" method="post">
<label for="form-check">Region</label>
<input class="form-check-input" type="checkbox" value="all" name="regions" id="allOffice" />
<label class="form-check-label" for="allOffice">Select All</label>
<input class="form-check-input" type="checkbox" value="region1" name="regions" id="region1"/>
<label class="form-check-label" for="region1">Region 1</label>
<input class="form-check-input" type="checkbox" value="region2" name="regions" id="region2"/>
<label class="form-check-label" for="region2">Region 2</label>
<input class="form-check-input" type="checkbox" value="region3" name= "regions" id="region3"/>
<label class="form-check-label" for="region1">Region 3</label>
<button type="submit" class="btn btn-primary">Test Checkboxes</button>
</form>
</div>
Controller
//Test the post method
@RequestMapping(value = "/cs-report", method = RequestMethod.POST)
public void printCheckboxValues(List<String> regions)
{
regions.foreach(s -> System.out.println(s));
}
Where, if this approach worked, the repo would look something like:
@Query("SELECT p FROM ProjectView p WHERE p.region IN 1")
List<ProjectView> findByRegion(@Param("regions") List<String> regions);
I think the controller uses the name attribute to reference the list of checkbox values, but I'm unclear on how to set up the controller to do this. Most of the examples I've seen have had the checkboxes bound to their parent object, and maybe that's what needs to be done. I have the checkboxes hard coded as there aren't that many and I don't expect the values to change in the database. But if I do need to bind the checkbox "region" values to the reportCs entity, an example of syntax would be greatly appreciated.
Any other suggestions on approach are greatly appreciated, and big bonus if the code can be generalized to take multiple params from multiple checkbox groups. Thank you.
Upvotes: 1
Views: 7130
Reputation: 31
OK, the checkboxes need to bind to a form-backing bean. It took some tinkering with the Thymeleaf syntax, but doing it this way is actually is quite convenient for binding multiple checkbox groups to multiple query parameters. Also, I've realized hard coding the checkbox values in the templates is a bad idea (Not loosely-coupled code and will create problems down the line) so my next step is to get the checkbox values dynamically from the database. Thank you for reading.
html:
<div class="form-check">
<form action="#" th:action="@{/cs-report}" th:object="${queryDto}" method="post">
<button type="submit" class="btn btn-primary">Get Report</button>
<input class="form-check-input" type="checkbox" value="all" name="all" id="all" />
<label class="form-check-label" for="all">Select All</label>
<input class="form-check-input" type="checkbox" value="region1" name="regions" th:field="*{regions}" id="region1" />
label class="form-check-label" for="region1">Region 1</label>
<input class="form-check-input" type="checkbox" value="region2" name="regions" th:field="*{regions}" id="region2" />
label class="form-check-label" for="region2">Region 2</label>
<input class="form-check-input" type="checkbox" value="region3" name="regions" th:field="*{regions}" id="region3" />
label class="form-check-label" for="region3">Region 3</label>
</form>
</div>
The DTO object:
// Form-backing bean to hold checkbox values on post submission
public class QueryDto {
private List<String> regions;
// Getter, setters
Controller
@PostMapping(value = "/cs-report")
public String testCheckboxes(@ModelAttribute QueryDto queryDto) throws IOException {
List<ReportDto> dtos = repository.findByRegion(queryDto.getRegions());
dtos.foreach(s -> System.out.println(s.getProjectRegion()));
}
Repository
@Query("SELECT p FROM ProjectView p WHERE p.region IN :regions")
List<ProjectView> findByRegion(@Param("regions") List<String> regions);
Upvotes: 2