PrettyFaces - wrong parameters via f:params tag - url-parameters

I can't figure out why the below code in the view:
<ui:repeat value="#{postsController.posts}" var="post">
<tr>
<td>#{post.title}</td>
<td>
<pretty:link mappingId="comments">
<f:param value="#{post.id}"/>
Comments
</pretty:link>
</td>
<td>
<pretty:link mappingId="edit">
<f:param value="#{post.id}"/>
Edit
</pretty:link>
</td>
<td>Delete</td>
</tr>
</ui:repeat>
and this one in the bean:
#Component
#ManagedBean
#RequestScoped
#URLMappings(mappings = {
#URLMapping(id = "posts", pattern = "/posts/", viewId = "/faces/posts/list.xhtml"),
#URLMapping(id = "edit", pattern = "/posts/#{postsController.id}/edit", viewId = "/faces/posts/edit.xhtml"),
#URLMapping(id = "new", pattern = "/posts/new", viewId = "/faces/posts/new.xhtml")
})
public class PostsController {
#Autowired
private PostService postService;
private List<Post> posts;
private Post post = new Post();
private Long id;
public List<Post> getPosts() {
return postService.findAll();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Post getPost() {
if(!post.isNew()) {
this.post = postService.findById(id);
}
System.out.println("++++++++++++++ getPost: " + post);
return post;
}
public void setPost(Post post) {
System.out.println("++++++++++++++setPost: " + post);
this.post = post;
}
public String create(final Post post) {
postService.save(post);
return "pretty:posts";
}
public String update(final Post post) {
System.out.println("++++++++++ update post: " + post);
postService.save(post);
return "pretty:posts";
}
}
The edit page:
<h:form>
<div class="row">
<div class="small-3 columns">
<label for="right-label" class="right inline">Title</label>
</div>
<div class="small-9 columns">
<h:inputText value="#{postsController.post.title}" required="true"/>
<p>
<h:commandButton value="Update" type="submit"
action="#{postsController.update(postsController.post)}"
styleClass="button radius"/>
</p>
</div>
</div>
</h:form>
give the wrong behaviour as follows:
when I edit an existing post, there is no title displayed in the texfield on the edit page, as if there is no ID passed from the parameters. However teh URL is correct: posts/1/edit.
after creating a new Post and hitting the edit link, the title of the Post is displayed correctly.
when I create another new Post, the title textfield id filled with one of the last Post.
update method, even it is called, keeps the original title value and never updates the existing Post.
What am I doing wrong here?
Thannk you.

Related

Spring-Boot form post not binding model

I'm kind of stuck because according to the documentation this should work, but upon posting my form, all my model fields are null. If anyone could point out what I'm missing would be great.
Here's my model:
package hello.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
#Entity
public class Post {
#Id
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
private String email;
#NotNull
private String content;
public Post() { }
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
#Override
public String toString() {
return "Post{" +
"email='" + email + '\'' +
", content='" + content + '\'' +
'}';
}
}
my thymeleaf fragment with the form:
<div xmlns:th="http://www.thymeleaf.org" th:fragment="wysiwyg" class="panel-body">
<form class="form-horizontal" role="form" method="post" action="#" th:action="#{/post}" th:object="post">
<div class="form-group">
<label for="email">Email</label>
<input id="email" type="email" class="form-control" placeholder="Enter email..." th:field="*{email}"
required/>
<small id="emailHelp" class="form-text text-muted">Enter your email address just in case.</small>
</div>
<div class="form-group">
<label for="content">Post</label>
<textarea id="content" class="form-control" th:field="*{content}"></textarea>
<small id="bodyHelp" class="form-text text-muted">Type your post in here.</small>
</div>
<button id="btnPost" class="btn btn-primary" type="submit">Submit</button>
</form>
<script>
$(document).ready(function() {
$('#teszt2').summernote({
"height": 200
});
});
</script>
</div>
and the appropriate controller method:
#PostMapping("/post")
public String testPost(Post post, Model model) {
System.out.println(post);
return "index";
}
The expected behavior would be to have Post{email=$email, content=$content}, however the only thing I get is: Post{email=null, content=null} and I don't understand why. What am I doing wrong? What am I missing? All the online tutorials claim that this just works but it clearly doesn't work for me.
#Update:
A bit of update. So I can get it working, but only like this:
#GetMapping("/post")
public String openEditor(Model model) {
model.addAttribute("post", new Post());
model.addAttribute("content", "wysiwyg");
return "index";
}
So I create a new Empty post. In my fragment I change the form from th:object="post" to th:object="${post}". Then in my PostMapping:
#PostMapping("/post")
public String testPost(#ModelAttribute("post") Post post, Model model) {
System.out.println(post); // Works well
return "index";
}
Why doesn't it work if I don't use ModelAttribute and don't create an empty Post in the first place. I expected it to work... clearly I was wrong
In my opinion you should not tie your Entity object directly with submit form.
I suggest to follow a standard practice to create a PostRequest class and use the fields you want to get from your form. Declare that in your #PostMapping method signature as #RequestBody PostRequest postRequest. Once you have the object in your controller method you can validate the input and create entity out of the object and then use it further for storing in your Database.
The practice is simple, do not mix your request/response objects with your database entity objects. That is risky too when you are storing any sensitive information in your database.
Not sure how this works with Thymeleaf templates, but there should be a way to follow this practice. Hope this helps.

Field is always null in Spring Boot Postmapping method

I want to bind my HTML table with all fields to Java code in Spring Boot.
Therefore, I have annotated my method with Postmapping, too.
I was already able to show all the fields in Thymeleaf and I was also able to set the checkBox value ( true / false ) accordingly.
This is my Thymeleaf HTML code:
<form action="#" th:action="#{/mitarbeiterverwaltung}" th:object="${users}" method="post">
<fieldset>
<table border="1" align="center">
<thead>
<tr>
<!-- <th:text = "#{mitarbeiterverwaltung.active}>Active ( Tick ) / Passive</th>-->
<th>Active ( Tick ) / Passive</th>
<th>ID</th>
<th>Username</th>
<th>Anzeigename</th>
<th>Dienstnummer</th>
</tr>
</thead>
<tbody>
<tr th:each="user, itemStat : *{users}">
<td><input th:field="*{users[__${itemStat.index}__].isActive}"
th:checked="${user.isActive}"
class="checkBox"
type="checkBox"
name="checkBox"
/></td>
<td><input th:field="*{users[__${itemStat.index}__].id}"
readonly/></td>
<td><input th:field="*{users[__${itemStat.index}__].username}"
readonly/></td>
<td><input class="anzeigename"
type="text"
name="anzeigename"
th:field="*{users[__${itemStat.index}__].anzeigename}"
th:id="${itemStat.index}"
readonly/></td>
<td><input class="dienstnummer"
type="text"
name="dienstnummer"
th:field="*{users[__${itemStat.index}__].dienstnummer}"
th:id="${itemStat.index}"
readonly/></td>
</tr>
</tbody>
</table>
<br />
<div style="text-align:center;">
<input type="submit" id="submitButton" th:value="Speichern"/>
</div>
</fieldset>
And this is my Java code, where the field isActive of UserCreationDto is always null.
#PostMapping
public String updateActivePassiveUser(#ModelAttribute UserCreationDto userTableSettings,
#RequestParam("checkBox") String checkBoxName, BindingResult result, Model model, Errors errors) {
logger.info("Method {} called in {}", new Object() {}.getClass().getEnclosingMethod().getName(), this.getClass().getName());
if (errors.hasErrors()) {
logger.error("Error in {}", new Object() {}.getClass().getEnclosingMethod().getName());
return "error";
}
List<Benutzer> users = userManagementServiceImpl.getAllUsers();
userManagementServiceImpl.updateActivePassiveUser(1, 0);
return "redirect:/mitarbeiterverwaltung?success";
}
Here is a picture of the field in Java code where the method is annotated with #PostMapping
And so does my #RequestMapping look like:
This is my #RequestMapping method:
#RequestMapping
public String showUserManagement(Model model) {
logger.info("Method {} called in {}", new Object() {}.getClass().getEnclosingMethod().getName(), this.getClass().getName());
List<Benutzer> users = userManagementServiceImpl.getAllUsers();
userForm = userManagementServiceImpl.saveUserForm(users);
model.addAttribute("users", userForm);
return "mitarbeiterverwaltung";
}
My UserCreationDto where all the fields get added to a list:
public class UserCreationDto {
private List<User> users = new ArrayList<>();
public void addUser(User user) {
this.users.add(user);
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
And my simple POJO class with all the fields
#Data
public class User {
//#SafeHtml prevents XSS ( Cross-Site Scripting )
#SafeHtml
private String username;
private String password;
private String anzeigename;
private String dienstnummer;
private long id;
private Boolean isActive;
}
The other fields like anzeigename, dienstnummer, id, and username are filled within my Java code, however, isactive is always null.
Maybe, someone can tell me what I am doing wrong here.
Thank you very much in advance.
I think you have to many options set. You don't need th:checked:
<input th:field="*{users[__${itemStat.index}__].isActive}"
class="checkBox"
type="checkBox"
name="checkBox" />
I found another way now, but it is not really nice.
#PostMapping
public String updateActivePassiveUser(#Valid #ModelAttribute("userForm") UserCreationDto userTableSettings,
#RequestParam List<String> searchValues, BindingResult result, Model model, Errors errors) {
The field searchValues contains all the checkBoxes that are ticked.
This is my view:
<td><input type="checkbox"
name="searchValues"
th:value="${user.id}"
th:checked="${user.isActive}"
/>
Now, the only problem that I am having is how to update my column that is of type Boolean in Postgresql?
For accomplishing this task I call this:
userRepo.updateActivePassiveUser(new Boolean("False"), id);
#Modifying
#Query(value = "UPDATE benutzer SET active = :active WHERE id = :id", nativeQuery = true)
#Transactional
void updateActivePassiveUser(#Param("active") Boolean active, #Param("id") long id);
However, the value in my database never changes.
Maybe, someone could give me a last hint, please!

How to pass loop paramters from JSP to Spring Controller

I am having a loop on my JSP page and want to pass these values in my Spring Controller. On every click on Retry button in JSP, I need all values in my controller for further processing. The code which I tried so far is:
Any help much appreciated.
JSP File
<table class="gridtable">
<tr>
<th>Queue Name</th>
<th>Retry Attempt</th>
<th>Reason for failure</th>
<th>Action</th>
</tr>
<% int i=0; %>
<c:forEach var="queueRowDetail" items="${queueRowDetailList}">
<tr>
<td>${queueRowDetail.queueName}</td>
<td>${queueRowDetail.attempt}</td>
<td>${queueRowDetail.errorDetails}</td>
<td>
<form:form method="post" action="/retry" id="frmFailure_<%=i%>" modelAttribute="queueRowDetail"/>
<form:hidden path="queueName<%=i %>" value="${queueRowDetail.queueName}"/>
<form:hidden path="attempt<%=i %>" value="${queueRowDetail.attempt}"/>
<form:hidden path="errorDetails<%=i %>" value="${queueRowDetail.errorDetails} "/>
<input type="button" value="Retry" onClick="sendobj(<%=i%>)" />
</form>
</td>
</tr>
<% i++; %>
</c:forEach>
function sendObj()
<script>
function sendobj(i)
{
var x = document.getElementById("frmFailure_"+i);
alert(obj);
alert("frmFailure_"+i);
x.submit();// Form submission
}
</script>
QueueRowDetail Class
package com.gartner.gqueuefailureapi.model;
public class QueueRowDetail {
private String queueName;
private String errorDetails;
private int attempt;
private Object payLoad;
public String getQueueName() {
return queueName;
}
public void setQueueName(String queueName) {
this.queueName = queueName;
}
public String getErrorDetails() {
return errorDetails;
}
public void setErrorDetails(String errorDetails) {
this.errorDetails = errorDetails;
}
public int getAttempt() {
return attempt;
}
public void setAttempt(int attempt) {
this.attempt = attempt;
}
public Object getPayLoad() {
return payLoad;
}
public void setPayLoad(Object payLoad) {
this.payLoad = payLoad;
}
}
InderController.Java
#RequestMapping(value = "/retry", method = RequestMethod.POST)
public String retryMessage( #ModelAttribute("queueRowDetail")QueueRowDetail queueRowDetail, ModelMap model) {
model.addAttribute("queuename", queueRowDetail.getQueueName());
return "success";
}

Spring + thymeleaf Validanting integer error Neither BindingResult nor plain target object for bean name available as request attribute

I'm in the process of learning spring and thymeleaf and working on a timekeeping project.
For this I need to validate the number of hours an employee clocks in one day.
I used the tutorial in the spring documentation for this however i keep getting the following error
Neither BindingResult nor plain target object for bean name 'timetable' available as request attribute
Any ideas what I might be doing wrong?
Controller class
#RequestMapping(value="Timetable/AddToTimetable", method = RequestMethod.GET)
public String newUser(Model md) {
md.addAttribute("assignments", serv.findAll());
return "AddToTimetable";
}
#RequestMapping(value = "/createEntry", method = RequestMethod.POST)
public String create(#RequestParam("assignmentId") int assignmentId,
#RequestParam("date") #DateTimeFormat(pattern = "yyyy-MM-dd") Date date,
#RequestParam("hoursWorked") int hoursWorked,
#Valid Timetable timetable, BindingResult bindingResult,
Model md) {
timetable = new Timetable();
timetable.setAssignmentId(assignmentId);
timetable.setDate(date);
timetable.setHoursWorked(hoursWorked);
md.addAttribute("timetables", service.timetableAdd(timetable));
if (bindingResult.hasErrors()) {
return "AddToTimetable";
}
return "redirect:/Timetable";
}
Service class
public BigInteger timetableAdd(Timetable timetable){
KeyHolder keyHolder = new GeneratedKeyHolder();
String sql = "INSERT INTO timetables ( assignmentId, date, hoursWorked) VALUES ( ?, ?, ?)";
template.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement pst = con.prepareStatement(sql, new String[] {"id"});
pst.setInt(1, timetable.getAssignmentId());
pst.setDate(2, new java.sql.Date(timetable.getDate().getTime()));
pst.setInt(3, timetable.getHoursWorked());
return pst;
}
}, keyHolder);
return (BigInteger) keyHolder.getKey();
}
}
Model class
package ro.database.jdbcPontaj.model;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
public class Timetable {
private int timetableId;
private int assignmentId;
private Date date;
private String project;
#NotNull
#Min(0)
#Max(12)
private int hoursWorked;
public int getTimetableId() {
return timetableId;
}
public void setTimetableId(int timetableId) {
this.timetableId = timetableId;
}
public int getAssignmentId() {
return assignmentId;
}
public void setAssignmentId(int assignmentId) {
this.assignmentId = assignmentId;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public int getHoursWorked() {
return hoursWorked;
}
public void setHoursWorked(int hoursWorked) {
this.hoursWorked = hoursWorked;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
public Timetable() {
}
public Timetable(int timetableId, String project, Date date, int hoursWorked) {
this.timetableId = timetableId;
this.project=project;
this.date = date;
this.hoursWorked = hoursWorked;
}
public Timetable(int timetableId, int assignmentId, Date date, int hoursWorked) {
this.timetableId = timetableId;
this.assignmentId = assignmentId;
this.date = date;
this.hoursWorked = hoursWorked;
}
}
Html
<form method="post" name="comment_form" id="comment_form" th:action="#{/createEntry}" th:object="${timetable}" role="form">
<p> Project</p><br>
<select name="assignmentId">
<option value="" th:each="assignment: ${assignments}" th:value="${assignment.assignmentId}" th:text="${assignment.assignmentId}"></option>
</select>
<p>Date</p> <br>
<input class="datepicker" type="text" name="date"><br>
<p>Number of hours</p>
<input type="text" name="hoursWorked" th:field="*{hoursWorked}"><br>
<p th:if="${#fields.hasErrors('hoursWorked')}" th:errors="*{hoursWorked}">Age Error</p>
<button type="submit" id="submit" class="btn btn-primary">Submit</button>
</form>
UPDATE:
Timetable (skipping bootstrap divs)
<div class="row">
<div class="col-md-10 title">
<h2>Timetable</h2>
</div>
<div class="col-md-2">
</div>
<div class="col-md-12">
<table class="table table-bordered">
<thead>
<tr>
<th>id</th>
<th>assignment</th>
<th>date</th>
<th>number of hours</th>
</tr>
</thead>
<tbody>
<tr th:each = "timetable: ${timetables}">
<td th:text="${timetable.timetableId}">45</td>
<td th:text="${timetable.project}">vasi</td>
<td th:text="${timetable.date}">1 ian</td>
<td th:text="${timetable.hoursWorked}">3000</td>
</tr>
</tbody>
</table>
Service method for Timetable
#Autowired
JdbcTemplate template;
public List<Timetable> findAll(String loginname) {
String sql = " SELECT timetables.timetableId, timetables.assignmentId, timetables.date, " +
"timetables.hoursWorked, users.username, projects.projectName AS project " +
"FROM timetables INNER join assignments on timetables.assignmentId = assignments.assignmentId " +
"INNER JOIN projects on assignments.projectId = projects.projectId " +
"INNER JOIN users on users.userId = assignments.userId where username= ?";
RowMapper<Timetable> rm = new RowMapper<Timetable>() {
#Override
public Timetable mapRow(ResultSet resultSet, int i) throws SQLException {
Timetable timetable = new Timetable(resultSet.getInt("timetableId"),
resultSet.getString("project"),
resultSet.getDate("date"),
resultSet.getInt("hoursWorked"));
return timetable;
}
};
return template.query(sql, rm, loginname);
}
The controller method for Timetable
#RequestMapping(value = {"/Timetable"}, method = RequestMethod.GET)
public String index(Model md){
org.springframework.security.core.Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String loginname = auth.getName();
md.addAttribute("timetables", service.findAll(loginname));
return "Timetable";
}
If I understand correctly you have two html pages one that shows all the assignments and one that you enter the new entry.I think that get the error when there is a validation error in the new entry page.
Substitute these lines
if (bindingResult.hasErrors()) {
return "AddToTimetable";
}
with these ones
if (bindingResult.hasErrors()) {
return "newEntry";//replace the newentry with the html page that you enter the new entry
}
When there is an error, you should go to the page that you tried to enter the new entry and not in the page that has all the assignments.

Springs form null values

I have a small problem with my form in spring, it is a simple form with title, username and password, validation works as if I try to submit an empty form I get an error however if I input values in and submit the form, it is submitted but it displays that values inserted are "null".
Domain:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
#NotNull
#Size(min=2, max=30)
private String username;
#NotNull
#Size(min=6)
private String password;
public String getTitle() { return title; }
public String getUsername() { return username; }
public String getPassword() { return password; }
public Long getId() { return id; }
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
public void setTitle(String title) { this.title = title; }
public List<Note> getNotes() {
return notes;
}
#OneToMany
private List<Note> notes = new ArrayList<Note>();
}
Form:
<form action="#" th:action="#{/create/user}" th:object="${user}" method="post">
<input type="hidden" th:field="*{id}" />
<p>Title: <input type="text" th:field="*{title}" /></p>
<p>Username: <input type="text" th:field="*{username}" /></p>
<p>Password: <input type="password" th:field="*{password}" /></p>
<p><input type="submit" value="Submit" /></p>
</form>
<div class="form-group">
<ol class="list-group">
<li class="list-group-item list-group-item-info" th:each="user : ${user}">
<span th:text=" ${user.title} + ${user.username} + ${user.password} "></span>
delete
</li>
</ol>
</div>
Controller:
#Controller
public class UserController {
#Autowired
protected UserService userService;
#RequestMapping(value = "/create/user", method = RequestMethod.POST)
public String createUser(Model model, #Valid #ModelAttribute("user") User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
model.addAttribute("user", user);
model.addAttribute("users", userService.findAll());
model.addAttribute("type", "danger");
model.addAttribute("message", "Please fill in all the fields" +
"Username needs to be at least 2 characters" +
"Passwords needs to contain at least 6 characters");
return "user";
}
userService.save(user);
model.addAttribute("user", new User());
model.addAttribute("cards", userService.findAll());
model.addAttribute("type", "success");
model.addAttribute("message", "A new user has been added");
return "user";
}
If any additional code in needed please let me know, I am still newbie in Spring
Found an error by reviewing the code:
In the form when outputting registered users it was:
<li class="list-group-item list-group-item-info" th:each="user : ${user}">
While it should be:
<li class="list-group-item list-group-item-info" th:each="user : ${users}">

Resources