How to submit table data in Spring Thymeleaf - java

I am following given link to submit table data to be saved in database
http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/
But the difference between given link and my implementation is that the front-end of link uses JSTL (JSP) while i am using Thymeleaf (HTML)
Below are the files being used
HTML Form :
<form method="POST" th:action="#{/updateAllRules}" th:field="${ruleForm}">
<table>
<thead>
<tr>
<th>S No</th>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr th:each="ruleModel,iteration : ${allRules}">
<td th:text="${ruleModel.id}"></td>
<td><input type="text" th:name="${'rule'+iteration.index+'.title'}" th:value="${ruleModel.title}"></td>
<td><input type="text" th:name="${'rule'+iteration.index+'.body'}" th:value="${ruleModel.body}"></td>
</tr>
</tbody>
</table>
<br>
<input type="submit" value="Update All">
</form>
Model Class :
public class Rule {
private Integer id;
private Date timestamp;
private String title;
private String body;
// constructor and Getter/Setters
}
Form Class :
public class RuleForm {
private List<Rule> rule;
public List<Rule> getRule() {
return rule;
}
public void setRule(List<Rule> rule) {
this.rule = rule;
}
}
Controller Method :
#RequestMapping(value = "/updateAllRules", method = RequestMethod.POST)
public String updateAllRules(#ModelAttribute("ruleForm") RuleForm ruleForm) throws IOException
{
System.out.println(ruleForm); // this prints com.web.model.RuleForm#235f9fcb
System.out.println(ruleForm.getRule()); //this prints null
return "redirect:/admin";
}
Please let me know what i am missing.
UPDATE 1:
Made changes as suggested. My new HTML form is as below
<form method="POST" th:action="#{/updateAllRules}" th:object="${ruleForm}">
<table>
<thead>
<tr>
<th>S No</th>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr th:each="rule,iteration : ${ruleForm}">
<td th:field="*{rule[__${iteration.index}__].id}"></td>
<td><input type="text" th:field="*{rule[__${iteration.index}__].title}"></td>
<td><input type="text" th:field="*{rule[__${iteration.index}__].body}"></td>
</tr>
</tbody>
</table>
<br>
<input type="submit" value="Update All">
</form>
On making these changes following exception is being received when the page loads.
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'rule' cannot be found on object of type 'java.util.ArrayList' - maybe not public?
Please see that i am sending original list in model attribute "ruleForm" on page load. Once page loads the data and user make changes , i want to POST complete table back to controller.

Forms should have a th:object, rather than a th:field:
<form method="POST" th:action="#{/updateAllRules}" th:object="${ruleForm}">
Instead of using th:name and th:value, you should instead be using th:field which does both of those for you. Fields also should be specified using the *{...} syntax, which assumes the th:object automatically.
<input type="text" th:field="*{rule[__${iteration.index}__].title}" />
Everything else looks correct to me.

Related

why is my form not passing information in spring boot

Hello i am passing information in a form and everything runs fine but when i fill out the form it doesnt not pass the information and i am getting this error.
There was an unexpected error (type=Internal Server Error, status=500).
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'movie_id' cannot be null
the code i am using is as folow:
#PostMapping("/save")
public String save(Movie movie) {
savedMovie.save(movie);
return "redirect:/LatestMovies";
}
And
<form th:action="#{/save}" method="post" >
<p><input type="text" id="movie_id" name="movie_id" value="" /></p>
<p><input type="text" id="movie_name" name="movie_name" value="" /></p>
<p><input type="submit" value="save" /></p>
</form>
i belive all other code is correct becuase if i try to render the db information i have no problem.
Update
This is the complete html code.
<div class="container">
<table class="table table-hover">
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr th:each="LatestMovies : ${latestMovies}">
<td th:text="${LatestMovies.id}"></td>
<td th:text="${LatestMovies.movieName}"></td>
<td>
<form th:action="#{/save}" method="post" th:object="${newMovie}">
<p><input type="text" id="movie_id" th:field="*{movie_Id}"/></p>
<p><input type="text" id="movie_name" th:field="*{movie_Name}"/></p>
<p><input type="submit" value="save" /></p>
</form>
</td>
</tr>
</table>
Your controller is expecting a Movie object, but it is receiving something else, which then produces a null Movie object. You need to use th:object in your form in order to correctly send the respective class. First, let's add a new #ModelAttribute to your controller, so that your form can automatically map your Movie object in your form.
Controller
// In order to use th:object in a form, we must be able to map a new entity to that form.
// In this case we return a Movie entity.
#ModelAttribute(value = "newMovie")
public Movie newMovie() {return new Movie();}
Now, let's change your form, so that it actually sends a Movie object.
<form th:action="#{/save}" method="post" th:object="${newMovie}">
<p><input type="text" id="movie_id" th:field="*{movie_id}"/></p>
<p><input type="text" id="movie_name" th:field="*{movie_name}"/></p>
<p><input type="submit" value="save" /></p>
</form>
Note that I also changed the name attribute in your inputs, for th:field. Have in mind that in order for this to work, the name of each field must match exactly the names in your objects.
Update
In case you want to set a default value to your form, without using js and since you can't combine th:field with th:value, you could set the object's attribute in your controller.
#ModelAttribute(value = "newMovie")
public Movie newMovie() {
Movie movie = new Movie();
movie.setName("Test");
return movie;
}
Update 2
If what you want is to put the current iteration of a Thymeleaf list in your form, you can do the following.
<div class="container">
<table class="table table-hover">
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr th:each="LatestMovies : ${latestMovies}">
<td th:text="${LatestMovies.id}"></td>
<td th:text="${LatestMovies.movieName}"></td>
<td>
<form th:action="#{/save}" th:object="${LatestMovies}" method="post">
<p><input type="hidden" th:value="*{id}"/></p>
<p><input type="hidden" th:value="*{movieName}"/></p>
<p><input type="submit" value="Submit"/></p>
</form>
</td>
</tr>
</table>
You forgot to mark method param with #RequestBody annotation.
This happens because the movie object you are trying to send from the form to the controller is not mapped properly. This has as a result the constraint of the movie_id you have in your movies table (PK not null I guess) to be violated by trying to insert a not null value into it. If you want the object formed in the frontend page form to be binded in a java object you could try this
front page form
<form:form action="save" modelAttribute="movie" method="POST">
<form:label path = "movie_id"> Movie id</form:label>
<form:input path="movie_id" name="movie_id">
<form:label path = "movie_name"> Movie name</form:label>
<form:input path="movie_name" name="movie_name">
<button type="submit">save</button>
</form:form>
(you should import on your page the springframework form taglib
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>)
Save controller code
#PostMapping("/save")
public String save(#ModelAttribute("movie") Movie movie) {
savedMovie.save(movie);
return "redirect:/LatestMovies";
}
Of course I am assuming that your object has a similar structure like shown below
Movie class
public class Movie{
private String movie_id; // or int or long
private String movie_name;
//getters setters constructors ommitted
}

Spring Thymeleaf - How to pass an objects ID from html page to the controller?

In my application the user is show a list of exam objects and they select one to edit.
When they click the edit link it brings them to a HTML page where the URL contains that exams ID.
I would like to know how to pass the id of the exam to the controller.
I'm getting this error: "Missing URI template variable 'id' for method parameter of type Long"
I would also like to know how to make existing values appear in the forms text boxses.
HTML allSubjects.html where the user selects a subject to edit: (this all works)
<h4> exams:</h4>
<div th:each="exam : ${subject.exam}">
<h4 th:text="${exam.examTitle}"/>
<a th:href="#{/editExam.html(id=${exam.examId})}">Edit Exam</a>
Edit exam HTML:
<form action="#" th:action="#{/editExam.html{examId}}" th:object="${exam}" method="put">
<table>
<tr>
<td> Exam Title:</td>
<td><input type="text" th:field="*{examTitle}" th:text="${exam.examTitle}"/></td>
<!-- <td th:if="${#fields.hasErrors('examTitlee')}" th:errors="*{examTitle}">error message</td> -->
</tr>
<tr>
<td> Exam grade worth </td>
<td><input th:field="*{examGradeWorth}" /></td>
<!-- <td th:if="${#fields.hasErrors('examGradeWorth')}" th:errors="*{examGradeWorth}">error message</td> -->
</tr>
<tr>
<td>examGradeAchieved</td>
<td><input th:field="*{examGradeAchieved}"/></td>
</tr>
<tr>
<td><button type="submit">Submit post</button></td>
</tr>
</table>
</div>
</form>
My controller:
#RequestMapping(value = "/editExam.html{examId}", method = { RequestMethod.GET, RequestMethod.PUT })
public String editExam(#ModelAttribute("exam") #PathVariable(value = "id")Long examId, #RequestBody Exam exam,Model model, BindingResult result) {
examRepository.findOne(examId);
model.addAttribute("examTitle", exam.getExamTitle());
model.addAttribute("examGradeWorth", exam.getExamGradeWorth());
model.addAttribute("examGradeAchieved", exam.getExamGradeAchieved());
exam.setExamTitle(exam.getExamTitle());
exam.setExamGradeWorth(exam.getExamGradeWorth());
exam.setExamGradeAchieved(exam.getExamGradeAchieved());
examRepository.save(exam);
return "editExam";
}
WHen I try to run all the above code just to see if the form displays I get the following error: Missing URI template variable 'id' for method parameter of type String

How to get checked boxes in a Controller

I have got a html page (with Thymeleaf):
<form action="#" th:action="#{/changeme}">
<fieldset>
<table style="width: 500px">
<tr th:each="esfield : ${esfields}">
<td>
<div>
<div class="checkbox">
<input type="checkbox" name="optionsMulti"
th:text="${esfield}" />
</div>
</div>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<button type="submit"
class="btn btn-xs btn-primary margin10-right paddingNew"
name="save">Calculate!</button>
</td>
<td></td>
</tr>
</table>
</fieldset>
</form>
When I click Calculate! it goes to my controller
#RequestMapping(value = "/changeme", params = { "save" })
public String save(final ModelMap m) {
m.addAttribute("centers", /*params*/);
return "clustering";
}
I would like to get information about checked boxes in my controller?
How can I do that?
Thank you in advance
You have basically two options :
either you use a different name for each checkbox
or you use spring tag <form:checkbox> instead of native <checkbox>
If you don't posted data will not allow you to know exactly what boxes were actually checked (excepted in cases all and none)
With the approach, you should use in your controller a #ModelAttribute annotated object containing a List<Boolean> and spring will automagically populate it with the values of your checkboxes.
#RequestMapping(value = "/changeme", params = { "save" })
public String save(#ModelAttribute BoxesForm form, final ModelMap m) {
// do what you need with form.getCheckboxes() ...
m.addAttribute("centers", /*params*/);
return "clustering";
}
public class BoxesForm {
List<Boolean> checkboxes;
// getter and setter omitted ...
}

FreeMarker Form for nested Object

I am trying to write freemarker template but could not able to parse with my object class.
My POJO is
public class Metrix {
#Id
String _id;
String loginId;
Date date;
List<MatrixDetail> headers;
//All getters and setters
}
public class MatrixDetail {
String header;
int time;
String detail;
//All getters and setters
}
//Controller after saving form
#RequestMapping(value = "/matrix/save", method = RequestMethod.POST)
public View saveMatrix(#ModelAttribute Metrix matrix, ModelMap model) {
System.out.println("Reachecd in matrix save" );
return new RedirectView("/TrackerApplication/header.html");
}
FTL template form part
<form name="matrix" action="matrix/save.html" method="post">
<table class="datatable" align:"center">
<tr>
<th>Login Id:</th> <th> <input type="text" name="loginId" value= ${matrixList.loginId} required /> </th>
</tr>
<tr> <td></td><td></td><td></td></tr>
<tr>
<th>Header</th> <th>Time</th> <th>Details</th>
</tr>
**// I am not getting how this nested object which is of type List<MatrixDetail>
// will get parse in my form.**
<#list matrixList.headers as header>
<spring:bind path = "MatrixDetail">
<tr>
<td> <input name = "header" value = ${header.header} /> </td>
<td> <input name = "time" value = ${header.time} /> </td>
<td> <input name = "detail" value = ${header.detail} /></td></tr>
</#list>
</table>
<input type="submit" value="Save" />
</form>
How can we write freemarker template for form processing of such kind of nested object?
I am getting issues in form submission.
I would strongly advise against this.
Forms might be displayable in email in some cases, but they may not always work in the email client, not to mention those that only ever read emails in text-only form won't be able to use them whatsoever.
If you need users to enter a form, link to a page on your site and have the form there instead.

Struts2 Populate Lists

I am having issues populating a List of User defined object attributes in Struts2.
Here is my example (getters / setters ommitted):
public class Foo { private String attr1; private String attr2;
}
public class Bar { private List foos; }
public class StrutsAction extends ActionSupport { private Bar bar; }
I have code in JSP as follows (extract):
<tr><td><input type="text" name="bar.foos.attr1"/></td><td><input type="text" name="bar.foos.attr2"</td></tr>
<tr><td><input type="text" name="bar.foos.attr1"/></td><td><input type="text" name="bar.foos.attr2"</td></tr>
I need each table row to create 1 foo item in the List with each attribute, however my code creats a new foo object for each attribute when passed through and I end up with 4 foos rather than 2!
I understand I can solve the problem with hard coding the Index into the html as follows:
<tr><td><input type="text" name="bar.foos[0].attr1"/></td><td><input type="text" name="bar.foos[0].attr2"</td></tr>
<tr><td><input type="text" name="bar.foos[1].attr1"/></td><td><input type="text" name="bar.foos[1].attr2"</td></tr>
I was wondering if it can be done without hard coding the Indexes?
I think you can use the Struts 2 iterator tag:
<s:iterator var="foo" value="bar.foos">
<tr><td><s:property value="attr1"/> .....
</s:iterator>
If it does not work, we can think in other solution.
You can find more information here: http://struts.apache.org/2.3.1/docs/iterator.html
[]s
You said you have variable number of rows. Let's name it numOfRows... Try to rewrite your example like so:
<table>
<s:iterator status="stat" value="(numOfRows).{ #this }" >
<tr>
<td><input type="text" name="bar.foos[%{#stat.index}].attr1"/></td>
<td><input type="text" name="bar.foos[%{#stat.index}].attr2"/></td>
</tr>
</s:iterator>
</table>
You can find docs about Iterator tag there... Look at the bottom of the page.
If you don't have numOfRows at render phase, then I am afraid the only way to generate textfields is javascript on client side....
[EDIT]
Regarding indexes... You don't have to bother if you have indexes something like this:
<table>
<tr>
<td><input type="text" name="bar.foos[0].attr1"/></td>
<td><input type="text" name="bar.foos[0].attr2"/></td>
</tr>
<tr>
<td><input type="text" name="bar.foos[3].attr1"/></td>
<td><input type="text" name="bar.foos[3].attr2"/></td>
</tr>
<tr>
<td><input type="text" name="bar.foos[5].attr1"/></td>
<td><input type="text" name="bar.foos[5].attr2"/></td>
</tr>
</table>
Struts will do the conversion just right... You just have to be aware, that objects who's indexes are missing will be null... So you should filter them out in the action.

Categories