Checking for Null Attribute in Thymeleaf Template - java

I am new to Thymeleaf and am trying to check for a null attribute in my template
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
The form submits fine if I am editing an existing user with an id already defined, however using the same form to add a new user I get the following
HTTP Status 400 - http://localhost:8080/myApp/saveUser/null"
My controller:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
My thought is if I can check for the null id I can plug in a unique one somehow. Better yet, if I could make use of the #GeneratedValue set on my User object's ID then I think I'd be in good shape
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
EDIT Including my user edit form method which returns the same form but pre-populated with the User's model Attributes
#RequestMapping(value = "/edit/user/{id}", method = RequestMethod.GET)
public ModelAndView getUserEditForm(#PathVariable Long id, Model model){
ModelAndView mav = new ModelAndView("userForm");
User user = userRepo.findOne(id);
mav.addObject("userForm", user);
return mav;
}
EDIT 2 Including my whole form (ID is "hidden" from user)
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
<input id="id" type="hidden" th:field="*{id}" />
<input id="id" type="text" th:field="*{name}" />
<input id="id" type="text" th:field="*{address}" />
<button id="save">Save</button>
</form>

As per discussion assuming that the following method is the one you call which should populate the user object and thus form fails on submission:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
The reason that method doesn't work is because you are potentially passing an empty user object to begin with.
To remediate this you need to implement checks to ensure object is not null prior to calling the page.
one solution could be:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
userRepo.save(user); //JPA Repo
if(user == null) // check if user object is empty
user = new User(); // if user is empty, then instantiate a new user object
model.addAttribute("user", user);
return "success";
}
The above will ensure that when you passing user object to the model, it is always available.

I've temporarily resolved this by creating a new #Controller to accept "null" at the end of the #RequestMapping (which kind of makes sense when creating a new user with a dynamic ID?), and just reimplementing the same logic. So when this is called
http://localhost:8080/myApp/saveUser/null"
It is mapped to my new Controller
#RequestMapping(value = "/saveUser/null", method = RequestMethod.GET)
public ModelAndView saveUser(#ModelAttribute("user") User user, Model model){
model.addAttribute("user", user);
userRepo.save(user);
return "success";
}

Related

Validate a field from form using the BindingResult :Spring

How to validate an integer type value in a form input using the BindingResult in spring?
When I try to validate the input I am getting an exception.I have gone through many posts but did not understand the concept completely.
The problem here isn't with validation, it is with the data binding. Data binding happens before validation, and validation is only invoked when all fields have been converted and bound correctly. In my case, the binding step is failing.Please correct me if I am wrong here.
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('age')}" th:errors="age"
class="validation-message"></label>
<input type="number" th:field="*{age}"
placeholder="Age" class="form-control" />
</div>
</div>
Code to map the request
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView createNewUser(#Valid User user, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
User userExists = userService.findUserByEmail(user.getEmail());
if (userExists != null) {
bindingResult
.rejectValue("email", "error.user",
"There is already a user registered with the email provided");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("registration");
} else {
userService.saveUser(user);
modelAndView.addObject("successMessage", "User has been registered succssfully");
modelAndView.addObject("user", new User());
modelAndView.setViewName("registration");
}
return modelAndView;
}
User.class
#Column(name = "age")
#NotEmpty(message = "*age is mandatory")
private Integer age;
Exception:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Size' validating type 'java.lang.Integer'. Check configuration for 'age'
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:229) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorNoUnwrapping(ConstraintTree.java:310) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getConstraintValidatorInstanceForAutomaticUnwrapping(ConstraintTree.java:244) ~[hibernate-validator-5.2.4.Final.jar:5.2.4.Final]
Not empty constraint is used for Strings and collections.
https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/javax/validation/constraints/NotEmpty.html.
To verify if an Integer has some value, #NotNull constraint should be used.

Get bean from select tag on jsp page

I use Spring, jsp and Hibernate in my project. I have two entities Employee and Department. Dapartment is a part of Employee and they have relationship one-to-many
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne
#JoinColumn(name = "dep_id")
private Department department;
I create controller, DAO and jsp pages for view.
My problem: I want to update data of Employee in my jsp page. Before this I add Employee and list of departments in model
In controller:
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
In JSP:
<form method="post">
<select value="${employee.department}">
<c:forEach items="${departments}" var ="dep">
<option value="${dep}">${dep.name}</option>
</c:forEach>
</form>
In controller (post request)
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String updateEmployee(#PathVariable("id") long id, Employee employee) {
employee.setId(id);
employeeDao.update(employee);
return "redirect:/employees";
}
but value employee.department=null Why?
Of course, on jsp page in "select" tag I can create variable dep I mean:
<select name ="dep">
<option value="${dep.id}">${dep.name}</option>
</select>
and then in controller using id of department i will be able to get department from database and update Employee. Is it right way?
You have not posted your full Controller however if we assume the initial get request looks like:
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView loadForEdit(#PathVariable("id") long id)
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
return new ModelAndView(...);
}
Then on loadForEdit() we load an Employee and Departments for edit and set them in the model for rendering the edit page.
Now, on submit, the POST method updateEmployee(...) knows nothing about this previous Employee and Department and therefore the framework is simply passing in a new instance to updateEmployee(...).
If you refactor along the lines of the following, then on the call to both the GET and POST handlers, the method annotated with #ModelAttribute(value = "employee") will execute. In the first case it will be added to the model, as previously, and in the second case the Employee will be retrieved, the fields bound to the updated values amd will then be passed to your POST handler.
#RequestMapping(value = "/{id}", method = RequestMethod.Get)
public String loadForEdit(){
return "nextView";
}
#RequestMapping(method = RequestMethod.POST)
public String updateEmployee(value = "/{id}", #ModelAttribute("employee") Employee employee)
{
return "redirect:/employees";
}
#ModelAttribute(value = "employee")
public Employee getEmployee(#PathVariable("id") long id)
{
return employeeDao.find(id);
}
#ModelAttribute(value = "departments")
public List<Department> getDepartments()
{
return departmentDao.list());
}
There is a lot of flexibility on how you can handle this:
See 'Using #ModelAttribute on a method argument' in:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method =
> RequestMethod.POST) public String processSubmit(#ModelAttribute Pet
> pet) { }
Given the above example where can the Pet instance come from?
There are several options:
It may already be in the model due to use of #SessionAttributes — see
the section called “Using #SessionAttributes to store model attributes
in the HTTP session between requests”.
It may already be in the model
due to an #ModelAttribute method in the same controller — as explained
in the previous section. [My suggestion]
It may be retrieved based on a URI template
variable and type converter (explained in more detail below).
It may be instantiated using its default constructor. [current position]
Sory, I forgot to publish the decision to solve. Thanks to all who helped me to find right solution.
In controller:
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getEmployee(#PathVariable("id") long id, Model model) {
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
return "employees/view";
}
Then we need to edit view. On JSP-page:
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<sf:form modelAttribute="employee" method="post">
<sf:select path="department" id="select-departments" >
<c:forEach items="${departments}" var="dep" varStatus="status">
<option value="${dep.id}">${dep.name}</option>
</c:forEach>
</sf:select>
</sf:form>
We also need to create department's editor:
public class DepartmentEditor extends PropertyEditorSupport {
private DepartmentDao departmentDao;
public DepartmentEditor(DepartmentDao departmentDao) {
this.departmentDao = departmentDao;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
long id = Long.parseLong(text);
Department department = departmentDao.find(id);
setValue(department);
}
}
And at the and we need to add some code in controller:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Department.class, new DepartmentEditor(departmentDao));
}
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String updateEmployee(#PathVariable("id") long id, #ModelAttribute Employee employee) {
employee.setId(id);
employeeDao.update(employee);
return "redirect:/employees";
}

How to pass model from view to controller in Spring?

I have a controller
#RequestMapping(value = "/admin/manage/{id}", method = RequestMethod.GET)
public ModelAndView goManage(#PathVariable int id) {
UserAccount userAccount = userAccountService.findUserAccount(id);
ModelAndView mav = new ModelAndView("admin/manage");
mav.addObject("userAccount", userAccount);
return mav;
}
and i pass userAccount model to manage.jsp view. In view I display this model. Example:
<div id="info">
<label>Login:</label><label>${userAccount.userDto.username}</label><br />
<label>Name:</label><label>${userAccount.userDto.firstName}
${userAccount.userDto.lastName}</label>
</div>
<form:form
action="${pageContext.request.contextPath}/admin/go"
modelAttribute="userAccount" method="post">
<input class="myButton" type="submit" value="Go" />
</form:form>
And it's ok, but I want pass this model userAccount from view further to next controller when I clicked button Go. My above form implementation doesen't work. How I can pass object from view? It's possible?
#RequestMapping(value = "/admin/go", method = RequestMethod.POST)
public ModelAndView goWithDrawalInvestment(
#ModelAttribute("userAccount") UserAccount userAccount) {
userAccount.setBalance(0);
mav.addObject("userAccount", userAccountDto);
return mav;
}

Spring MVC user data update issue

I’ve an issue with update of user data in Spring MVC app.
So, I have user and I have FORM filled with data from JSP. Now data from FORM overrides all fields of user data with null, except entered in jsp.
In another case – user’s data overrides form’s data.
Help me, please, to do it correctly. I’ve tried a lot of variants but nothing works.
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView updateView(#ModelAttribute(value = "updateForm")
HttpSession session) {
User user = (User) session.getAttribute("user");
UserForm updateForm = new UserForm();
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
#RequestMapping(method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "updateForm")
UserForm updateForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
model.addAttribute("updateForm", updateForm);
if (result.hasErrors()) {
return "profileupdate";
}
if (!updatingUser(updateForm, model, user))
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
return "updated";
}
private boolean updatingUser(UserForm updateForm, Model model, User user) {
fillForm(updateForm, user);
user = updateForm.getUser();
//Another case
//user = updateForm.getUser();
//fillForm(updateForm, user);
return userService.updateUser(user);
}
private void fillForm(UserForm updateForm, User user) {
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
}
}
** UserForm class**
public class UserForm {
private Integer userId;
private String login;
private String name;
private String password;
private String email;
public UserForm() {
}
public User getUser() {
User user = new User();
user.setUserId(userId);
user.setLogin(login);
user.setPassword(password);
user.setName(name);
user.setEmail(email);
return user;
}
public void setUser(User user) {
this.userId = user.getUserId();
this.login = user.getLogin();
this.password = user.getPassword();
this.name = user.getName();
this.email = user.getEmail();
………………………….
getters and setters
}
This is my DAO and Service
#Override
public boolean updateUser(User user) {
return userDao.updateUser(user);
}
#Override
#Transactional
public boolean updateUser(User user) {
if (isUserExists(user)) {
return false;
}
currentSession().update(user);
return true;
}
Updade.jsp
<sf:form name="login"
method="POST"
action="${app}/edit"
modelAttribute="updateForm"
enctype="application/x-www-form-urlencoded">
<label for="login">Login:</label><br>
<input name="login" id="login" type="text" value=""/> <br>
<sf:errors path="login" cssClass="error"/><br>
<br><label for="password">Password:</label>
<br><input name="password" id="password" type="password" value=""/>
<br>
<sf:errors path="password" cssClass="error"/><br>
<br> <input type="submit" name="submit" value="Update"/>
</sf:form>
It would be very hard for spring or hibernate to guess what values are null because user wants them to be null and what are null because they do not have to be touched. You as the progammer have to supply a fully completed object.
There are two common ways to do that :
you suppose null fields should be left untouched and modify fillform accordingly :
if (updateForm.getUserId().isEmpty()) { updateForm.setUserId(user.getUserId()); }
...
you prefil your form with current User value in the get that precedes the post (more common unless you need a post without the get part before)
EDIT
To prefill the form (the jsp part seems to be fine) your controller should put a filled UserFormin the model in the GET method.
#RequestMapping(method = RequestMethod.GET)
public String updateView(#ModelAttribute(value = "updateForm")
UserForm updateForm,
HttpSession session) {
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return "profileupdate";
}
with updateForm being implicitely in model due to the #ModelAttribute annotation, or
#RequestMapping(method = RequestMethod.GET)
public ModelAndView updateView(HttpSession session) {
updateForm = new UserForm();
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
I also removed the value="/edit" because it is not on updateUserProcess and I assumed the "/edit" was allready established on controller.
Well, main problem was in the path on JSP. Instead of handle request through controller I've set just a link to the page. So, advice - be careful and attentive with mappings.
WRONG VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/updatepage.jsp"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
RIGHT VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/edit"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
And controller
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String updateView(UserForm userForm,
HttpSession session,
ModelMap model) {
User user = (User) session.getAttribute("user");
userForm.setUser(user);
model.addAttribute("userForm", userForm);
return "profileupdate";
}
#RequestMapping(value = "/edit.do", method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "userForm")
UserForm userForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
session.getAttribute("userForm");
model.addAttribute("userForm", userForm);
userValidator.validate(userForm, result);
if (result.hasErrors()) {
logger.error("Validation error");
return "profileupdate";
}
if (!updatingUser(userForm, model, user)) {
logger.error("User update error!");
logger.error("Login or Email is already in use!");
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
logger.info("User updated!");
return "newuserprofile";
}
Have you checked the values of user.getUserID,user.getLogin(),user.getPassword(),user.getEmail()
in the following segment of code? Is it null or the data you recieved in the model object User?
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
Please post the code for userService.updateUser(user) so that we can understand more.

How to pass ID to controller?

I have a form
<form method="POST" action="/user/${id}">
<input type="text" name="id" value="${id}" placeholder="Input Id">
<button>Get User</button>
</form>
How to pass id to controller?
#RequestMapping (value = "/user/{id}", method = RequestMethod.POST)
public String getStudent(#PathVariable ("id") Integer id, Model model){
User savedUser = userRepository.get(id);
model.addAttribute("user", savedUser);
return "user";
}
You could do this way , consider i am passing the ${id} value through the query string
Get User
And in your controller,
#RequestMapping ("user")
public String getStudent(#RequestParam Integer id, Model model){
User savedUser = userRepository.get(id);
model.addAttribute("user", savedUser);
return "user";
}
Hope this helps !

Categories