Thymeleaf .html passing parameter to MVC Spring Controller Java - java

im new to thymeleaf and Spring - im doing some small project to my school class and i have a problem with html link. I had troubles with it for 2hours now and im editing my code in 10 diffrent ways so please answer how to do it properly. Below its my html template (i tried using here th:href with two diffrent ways:
<table>
<th>Name</th>
<th>Date</th>
<th>See</th>
<th>Edit</th>
<a th:each=" i : ${workouts}" varStatus="status">
<tr>
<td><a th:text="${i.getName()}"/></td>
<td><a th:text="${i.getDate()}"/></td>
<td>See</td>
<td>See</td> </tr>
</a>
</table>
And here its my Controller:
#Controller
public class SeeWorkoutController {
#RequestMapping(value = "/seeWorkoutPage/{id}", method=RequestMethod.GET, params="id")
public String test2(#PathVariable("id")Long id, Model model) {
System.out.println(id);
return "workoutsPage";
}
but it's now working properly. Please help.

Check the documentation for Thymeleaf Link URLs. In particular this example:
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
view
I think you're almost there with your second example, but it should read
th:href="#{/seeWorkoutPage/{workoutId}(workoutId=${i.getId()})}"
Note how you specify the {workoutId} parameter, then give it's value between the brackets afterwards.

Try:
<span th:each="workout: ${workouts}">
<td><a th:text="${workout.name}"/></td>
<td><a th:text="${#dates.format(workout.date,'MMM d, yyyy')}</td>
<!-- add other stuff when you have the above working -->
</span>
You don't need the get() syntax (and it can throw an exception in some containers). Also, it looks like you're nesting anchor (<a>) tags too.

Related

Why does this Spring Boot/Thymeleaf button hide my data or not work at all?

I'm curently trying to get into Spring Boot and Thymeleaf by following tutorials and playing around with the code they provide. I tried implementing an extra functionality and had the following problem: The code should display a ToDo-List consisting of ToDo-Objects that each have a name, description and a boolean status that denotes if they are done or not. Each line of the table is supposed to have a button to click in order to mark a ToDo as done.
<table style="border-collapse:collapse; font-family: Arial,Arial,sans-serif;">
<tr>
<th padding: 5px"></th>
<th> To-Do</th>
<th> Description</th>
<th> Done?</th>
</tr>
<tr th:each="todo : ${todos}">
<td>
<!-- <form method="POST" th:action="#{/updateDone(exactToDo=${todo})}">
<button type="submit" name="submit" value="value" class="link-button">Done</button>
</form> -->
<form method="POST" th:action="#{/updateDone}">
<input type="hidden" name="exactToDo" id="exactToDo" th:value="${todo.getName()}" />
<button type="submit" name="submit" value="value" class="link-button" >This is a link that sends a POST request</button>
</form>
</td>
<td th:utext="${todo.name}" style="border: 1px solid black;">...</td>
<td th:utext="${todo.description}" style="border: 1px solid black;">...</td>
<td th:text="${todo.done} ? 'Yes! ' : 'No' " style="border: 1px solid black;">...</td>
</tr>
</table>
Displaying the contents works, but my button does nothing in this configuration. I followed the directions from this older question, but it doesn't change the status of my ToDos. I can see in the Browser console that it sends a Post request, but there seems to be nothing inside.
The part that is currently commented out makes the data disappear from my table, only leaving the first line with the headers. I tried various similar approaches that I found online, but they all had one of those two results.
Here is the code of the relevant controller:
#RequestMapping(value = {"/updateDone"}, method=RequestMethod.POST)
public String completeTask(Model model, #RequestParam("exactToDo") String exactToDo){
for (ToDo todo : todos){
if (todo.getName().equals(exactToDo)){
todo.markDone();
}
}
return "list";
}
Setting return to "redirect:/list" fixes the problem with the "disappearing" data, but the status of the ToDo still doesn't change. I assume that the problem is that the ToDo object isn't sent to the method correctly, but I'm not exactly sure why. You might have noticed that I am trying to send todo.getName() instead of the ToDo Object directly, that is because when I try to send the object, I get an error in the second line of my posted Controller code, telling me that a String couldn't be converted into a ToDo-Object (the Controller was of course configured to take a ToDo object as parameter). This is why I think the problem has to be somewhere there.
I'd be very grateful if someone could help me fix this problem or point me to a better way of having a button on an HTML-page activate a method in my Java-Code. Tips for good learning resources are also greatly appreciated. I'm trying to learn Spring and Thymeleaf in order to make User Interfaces for Java programs.
Thank you for your time!
First thing First.
Why would you need to write so much (form) just for a button? Now if you "must" use form then you are missing the th:object="${todo}" in your form tag. It will help Controller to understand on what object you are going to take some action.
But I would suggest you use this inside your 'td' block instead of a form. It will do the same job for you. Once your request is successful, you can redirect it to your list and you should see the new results getting reflected immediately.
Mark Done
You can refer this to see a full blown example. Here you will find two things. User Controller and Task Controller that might interest you.
For anyone who might find this later, here is the answer:
The link Ajay Kumar posted ended up working, with a little tweak to include the necessary parameter:
<a href="/updateDone" th:href="#{/updateDone(exactToDo=${todo.getName()})}" >Mark Done</a>

GET method sent instead of DELETE

I am trying to write the frontend of an application, and I've run into a problem. I've been trying to realize a DELETE method using AJAX, but according to Spring a GET is sent when I run the code.
HTML code:
<tr th:each="attraction : ${attractions}" th:object="${attraction}">
<td th:text="*{name}"></td>
<td th:text="*{latitude}"></td>
<td th:text="*{city}"></td>
<td><a th:href="|/edit/*{id}|">EDIT</a></td>
<script>
function sendDelete(event) {
xhttp.preventDefault();
xhttp.open("DELETE", this.href);
xhttp.send();
}
</script>
<td><a th:href="|/delete/*{id}|" onclick="sendDelete(event);">DELETE</a></td>
</tr>
Spring code:
#DeleteMapping("/delete/{id}")
String delete(#ModelAttribute Attraction attraction) {
attractionService.delete(attraction);
return "redirect:/";
}
How could I solve this problem? Thank you in advance.
You went the long way around to fix this.
Link tags can send whatever http method you wish, as long as you are handling it in JavaScript and you call preventDefault.
But you have to do it on the event passed into the click handler not on the xhttp pbject. So on your event handler you should have done
event.preventDefault()
and not:
xhttp.preventDefault()
Your form hack isn't idiomatic. It'll freak out the next person who works on that code!
With some help, I could figure out the problem. The basic problem is that the
< a > tag is only able to handle GET methods.
Instead that part of my code, I sorted it out like this in HTML:
<td>
<form th:method="DELETE" th:action="|/delete/*{id}|">
<input type="submit" value="Send">
</form>
</td>
Now it works perfectly.

List all available model attributes in Thymeleaf

For debugging purposes I would like to list all model attributes available to my thymeleaf template while it is rendering.
Something like:
<table>
<tr th:each="model : ${*}">
<td th:text="${model}"></td>
</tr>
</table>
But obviously that's nonsense, and I get a well-deserved error.
(org.springframework.expression.spel.SpelParseException: EL1070E:(pos 0): Problem parsing left operand)
Is there a way of outputting such debug information? I'd settle even for some logging output.
Or, does Thymeleaf provide something similar to Struts 2's struts.devMode where it added a debug section at the bottom of the page listing all available properties?
Try this:
<table>
<tr th:each="var : ${#vars}">
<td th:text="${var.key}"></td>
<td th:text="${var.value}"></td>
</tr>
</table>
The accepted answer does not seem to work for Thymeleaf 3; here's an update. Please note that I'm using Spring; this might not work for non-Spring apps.
<table>
<tr th:each="var : ${#vars.getVariableNames()}">
<td th:text="${var}"></td>
<td th:text="${#vars.getVariable(var)}"></td>
</tr>
<!--
Adding these manually because they are considered special.
see https://github.com/thymeleaf/thymeleaf/blob/thymeleaf-3.0.3.RELEASE/src/main/java/org/thymeleaf/context/WebEngineContext.java#L199
-->
<tr>
<td>param</td>
<td th:text="${#vars.getVariable('param')}"></td>
</tr>
<tr>
<td>session</td>
<td th:text="${#vars.getVariable('session')}"></td>
</tr>
<tr>
<td>application</td>
<td th:text="${#vars.getVariable('application')}"></td>
</tr>
</table>
That said, what I've done is created a standalone Bean that makes things a bit prettier and dumps to logs instead of to HTML:
#Component
public class ThymeleafDumper {
private Logger log = LoggerFactory.getLogger(ThymeleafDumper.class);
public void dumpToLog(WebEngineContext ctx) {
log.debug("Thymeleaf context: {}", formatThisUpNicely(ctx));
}
// ... etc
}
Where formatThisUpNicely can use ctx.getVariableNames(), put the results into a SortedMap, export to json, whatever. Don't forget those three 'special' variables!
Then expose an instance of it as a #ModelAttribute in a Controller or a ControllerAdvice:
#ControllerAdvice
public class SomeControllerAdvice {
#Autowired
private ThymeleafDumper thymeleafDumper;
#ModelAttribute("dumper")
public ThymeleafDumper dumper() {
return this.thymeleafDumper;
}
}
Then in my template run:
<div th:text="${dumper.dumpToLog(#vars)}"/>
these are all the logging available configurations :
log4j.logger.org.thymeleaf=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.TIMER=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.cache.FRAGMENT_CACHE=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.cache.MESSAGE_CACHE=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE=DEBUG
these will log all the thymeleaf actions. I hope it is helpful.
Thymeleaf 3.0:
To put it short, try the following:
<table>
<tr th:each="var : ${param.keySet()}">
<td th:text="${var}"></td>
<td th:text="${param.get(var)}"></td>
</tr>
</table>
When a #Controller returns some value stored in a Spring Model, the values are actually stored as request parameters, which is accessible using:
request.getParameter("paramName");
Thymeleaf provides a similar functionality: "Web context namespaces for request/session attributes", the syntax is as stated.
The Thymeleaf built-in ${param} kewords returns an WebRequestParamsVariablesMap extends VariablesMap<String,String[]> object,
whose keySet() method returns a String[], which contains those variable names within a httprequest.

Spring MVC Application combine form using GET method and URL with path Varibles

I am developing an MVC web app using spring framework.
At some point I have a jsp that is listing study case objects as sown below
<tbody>
<c:forEach items="${studycases}" var="studycase">
<tr>
<td>
<a href='<spring:url value="/studycase/${studycase.study_case_number}.html"/>'>
${studycase.study_case_number}
</a>
</td>
<td>
${studycase.dateOFHospitalAdmission}
</td>
<td>
${studycase.dateOfWardAdmission}
</td>
<td>
${studycase.dateOfWardDischarge}
</td>
</tr>
</c:forEach>
</tbody>
as you can see there is a spring:url that is directing to a jsp with details about the spesific study case, that is being handled by the below controller :
#RequestMapping("/studycase/{studyCaseNumber}")
public String detail(Model model, #PathVariable String studyCaseNumber)
{
model.addAttribute("studyCase", studyCaseService.findOne(studyCaseNumber)) ;
model.addAttribute("measurements", measurementService.findAllOfThem(studyCaseNumber)) ;
return "study-case-detail" ;
}
The problem is that as the study cases listed in the first jsp could be thousands I will need to make it possible for the user to enter the study_case_number of a study case in a input field and get the details of the study case having the study case number inputed . So what I am doing is this:
<form action="<spring:url value="/studycase/study-case-detail2"/>" method="GET">
Study Case Number : <input type="text" name="studyCaseNumber">
<br/>
<input type="submit" value="Submit" />
</form>
That is being handled by an other controller and directs to an other jsp with more or less the same structure and data :
#RequestMapping("/studycase/study-case-detail2")
public String detail2(Model model, #RequestParam("studyCaseNumber") String std)
{
model.addAttribute("measurements", measurementService.findAllOfThem(std)) ;
return "study-case-detail2" ;
}
My question is this : Is this a good way to go having different controllers and different views even if they are presenting more or less the same thing ? What are other alternatives ??
Is there any source that you can direct me to containing best practices catalogue or guide on how to handle similar situations ??
Looking at your code, your controllers are not doing exactly the same thing (e.g. different model attributes, and they return different views).
Other than that, in general, it's not the best practice to do what you're doing as it somewhat in conflict with "Do Not Repeat Yourself" principle (also known as DRY principle).
One suggestion would be to use javascript to do redirect on id from input box rather than a form.
It seems you're using Spring Data too. If that's the case then one additional (not exactly related) suggestion to DRY up your code would be to use Spring Data's domain class converters to avoid calls studyCaseService.findOne(studyCaseNumber) in your StudyCase related controllers.
With domain class converters in place, you could then write controller method like this:
public String detail(Model model, #PathVariable("studyCaseNumber") StudyCase studyCase)
and avoid call to repository's findOne method as the converters would automatically convert ID to entity.
Take a look at DomainClassConverter in "Basic Web Support" section of Spring Data manual:
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

Why is my post not give data to my controller from my form?

I am developing a Sprin MVC application and I have a form containing a table in one of the UI jsp's, (welcome.jsp) and when the submit button is clicked, I am trying to print out the data in the form to the web applications console.From there i intend to parse the checkboxes that are selected and then have the controller send the 'selected' data back to the databased to be updated to the next status in the applications flow.
So far the form is 'successfully' posting as in no error or exceptions is being thrown, but the printed statement in the console is blank which makes me think that no data is being sent, and I would greatly welcome any help to fix this.
Here is the setup of what I have, not the actual code but a rough set up of the elements and methods.
welcome.jsp:
<form action="<c:url value="/postPage" />"method="post" modelAttribute="rTable">
<br/>
<table>
<thead>
<tr>
<th>title1</th>
<th>title2</th>
<th>title3</th>
<th><select>
<option>option1</option>
<option>option2</option>
</select></th>
</tr>
</thead>
<tbody>
<tr>
<td>value1</td>
<td>value1</td>
<td>value1</td>
<td><input type="checkbox" value="row_data_id" /></td>
</tr>
</tbody>
<tfoot>
<tr><td colspan="4"></td>
</tfoot>
</table>
<br/>
</form>
My controller has the following method in it with all the necessary libraries imported:
controller.java
#RequestMapping(value="/postPage", method = RequestMethod.POST)
public String processUpdate(#ModelAttribute("rTable") String table, ModelMap model) {
System.out.println(table);
return "postPage";
}
The console line that is print is this:
.
.
.
[3/19/14 16:36:53:625 EDT] 0000006a SystemOut O
.
.
.
Does anyone know why this is not printing anything? Am I really not successfully sending anything to the controller?
After a good deal of reading and trial and error I found my answer. I will explain in terms with spring framework forms. In order to pass data through the form from front-end to back end, first every input will need to be tied to the form using the spring form JSTL tag
ex.
form => form:form
input=> form:input
in the form:form it isn;t necessary but you should have a modelAttribute that is linked to a java class, then in each input their need to be a path attribute the is linked to a variable in the modelAttribute class and a value to assign to the variable. Then on a submit the values are linked to the back end via the getters and setters on the java class to be used in the back-end. I hope I am explained that clearly.

Categories