I am writing form that lets user to change his password. Instead of passing User object to form, I am passing 3 empty Strings. Everything is okay, but when I am passing Submit, Strings returns as empty. Is there any way to get String from forms in Spring without packaging them into objects like changePasswordForm with 3 String fields?
My code:
Change password view:
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<head th:replace="/fragments/head"></head>
<body>
<div class="container">
<div th:replace="/fragments/header"> </div>
<div class="jumbotron">
<h1 class="display-4">Changing password</h1>
<form action="#" th:action="#{/user/changePassword/} + ${user.id}" method="post">
<div class="form-group">
<label>Current password</label>
<input type="password" th:field="${currentPassword}" class="form-control" placeholder="Your current password"/>
</div>
<div class="form-group">
<label>New password</label>
<input type="password" th:field="${newPassword}" class="form-control" placeholder="Your new password"/>
</div>
<div class="form-group">
<label>New password confirmation</label>
<input type="password" th:field="${newPasswordConfirmation}" class="form-control" placeholder="Confirm your new password"/>
</div>
<button class="btn btn-primary">Submit</button>
<button class="btn btn-secondary">Reset</button>
</form>
</div>
<div th:replace="/fragments/footer"> </div>
</div>
</body>
</html>
User controller:
package io.gromo13.personalBlog.controller;
import io.gromo13.personalBlog.service.RoleService;
import io.gromo13.personalBlog.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import io.gromo13.personalBlog.model.User;
import javax.validation.Valid;
#Controller
#RequestMapping("/user")
#SessionAttributes("user")
public class UserController {
#Autowired
UserService userService;
#Autowired
RoleService roleService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setRoleService(RoleService roleService) {
this.roleService = roleService;
}
#GetMapping("/{id}")
public String profile(#PathVariable Long id, Model model) {
User user = userService.get(id);
model.addAttribute("user", user);
return "/user/profile";
}
#GetMapping("/register")
public String register(Model model) {
model.addAttribute("roles", roleService.getAll());
model.addAttribute("user", new User());
return "/user/register";
}
#PostMapping("/register")
public String registerSubmit(#Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors())
return "/user/register";
userService.add(user);
return "redirect:/admin/users";
}
#GetMapping("/edit/{id}")
public String edit(#PathVariable Long id, Model model) {
User user = userService.get(id);
model.addAttribute("user", user);
model.addAttribute("password", "");
return "/user/edit";
}
#PostMapping("/edit/{id}")
public String editSubmit(#ModelAttribute("user") #Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors())
return "/user/edit";
userService.edit(user);
return "redirect:/admin/users";
}
#GetMapping("/changePassword/{id}")
public String changePasword(Model model) {
model.addAttribute("currentPassword", "");
model.addAttribute("newPassword", "");
model.addAttribute("newPasswordConfirmation", "");
return "/user/changePassword";
}
#PostMapping("/changePassword/{id}")
public String changePasswordSubmit(#PathVariable Long id,
#ModelAttribute("currentPassword") String currentPassword,
#ModelAttribute("newPassword") String newPassword,
#ModelAttribute("newPasswordConfirmation") String newPasswordConfirmation,
BindingResult bindingResult) {
if (bindingResult.hasErrors())
return "/user/changePassword";
User user = userService.get(id);
if (newPassword.equals(newPasswordConfirmation) && user.getPassword().equals(currentPassword)) {
user.setPassword(newPassword);
userService.edit(user);
}
return "redirect:/admin/users";
}
#GetMapping("/delete/{id}")
public String delete(#PathVariable Long id) {
userService.delete(id);
return "redirect:/admin/users";
}
}
Actually my whole page is working fine, I just cannot read single Strings passed to forms without wrapping to objects.
Every tutorial about forms in Spring I found is passing object like xForm or User to view and read them. It works for me too, but I do not see sense in creating special object just for single form.
change th:field to name tag
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<head th:replace="/fragments/head"></head>
<body>
<div class="container">
<div th:replace="/fragments/header"> </div>
<div class="jumbotron">
<h1 class="display-4">Changing password</h1>
<form action="#" th:action="#{/user/changePassword/} + ${user.id}" method="post">
<div class="form-group">
<label>Current password</label>
<input type="password" name="currentPassword" class="form-control" placeholder="Your current password"/>
</div>
<div class="form-group">
<label>New password</label>
<input type="password" name="newPassword" class="form-control" placeholder="Your new password"/>
</div>
<div class="form-group">
<label>New password confirmation</label>
<input type="password" name="newPasswordConfirmation" class="form-control" placeholder="Confirm your new password"/>
</div>
<button class="btn btn-primary">Submit</button>
<button class="btn btn-secondary">Reset</button>
</form>
</div>
<div th:replace="/fragments/footer"> </div>
</div>
</body>
</html>
remove this from controller
#GetMapping("/changePassword/{id}")
public String changePasword(Model model) {
model.addAttribute("currentPassword", ""); //remove
model.addAttribute("newPassword", ""); //remove
model.addAttribute("newPasswordConfirmation", ""); //remove
return "/user/changePassword";
}
and here change ModelAttribute to RequestParam Annotation
#PostMapping("/changePassword/{id}")
public String changePasswordSubmit(#PathVariable Long id,
#RequestParam("currentPassword") String currentPassword,
#RequestParam("newPassword") String newPassword,
#RequestParam("newPasswordConfirmation") String newPasswordConfirmation) {
this sentence bindingResult not work because this validate a POJO using validations annotations
//if (bindingResult.hasErrors())
// return "/user/changePassword";
for this case the best way is create a new POJO with those fields and then validate them.
if you want to use the validation you need to create a POJO and add the constraints.
1)
public class ChangePass {
#Size(min = 8,max = 13, message = "error current password between {min} to {max}")
private String currentPassword;
#Size(min = 8,max = 13,message = "error new password between {min} to {max}")
private String newPassword;
//validate later
private String newPasswordConfirmation;
}
2)
then you need to add a instance of this POJO in the view.
#GetMapping("/changePassword/{id}")
public String changePasword(Model model) {
model.addAttribute("userPass", new ChangePass()); // adding model
return "/user/changePassword";
}
3)
in your form you need to fix 2 things add
th:object="${userPass}" and add th:field="*{currentPassword}" not th:field="${currenctPassword}" the difference is " * "
<form th:action="#{/user/changePassword/} + ${user.id}" method="post" th:object="${userPass}">
<div class="form-group">
<label>Current password</label>
<input type="password" th:field="*{currentPassword}" class="form-control" placeholder="Your current password"/>
</div>
<div class="form-group">
<label>New password</label>
<input type="password" th:field="*{newPassword}" class="form-control" placeholder="Your new password"/>
</div>
<div class="form-group">
<label>New password confirmation</label>
<input type="password" th:field="*{newPasswordConfirmation}" class="form-control" placeholder="Confirm your new password"/>
</div>
<button class="btn btn-primary">Submit</button>
<button class="btn btn-secondary">Reset</button>
</form>
4)
and the Post Method should be...
#PostMapping("/changePassword/{id}")
public String changePassword(#PathVariable Long id,
#ModelAttribute("userPass"),
#Valid ChangePass change,
BindingResult errors){
if(errors.hasErrors()){
return "/user/changePassword";
}
//your next code
Related
I am trying to make a Book Management project where I have three buttons on the home.jsp page. Each button redirects to a separate page and each of these pages has a form. I have one Controller class that has three methods to handle each form submissions from each of these pages but when I try to use #ModelAttribute in the JSP page for any form, I am unable to get the value that I add to the Model.
Here is the home.jsp:
<body>
<div class="container">
<div>
<h1>Spring Boot Web JSP Example</h1>
<h2>Name: ${book.name}</h2>
<h2>Author: ${book.author}</h2>
<h2>ISBN: ${book.isbn}</h2>
</div>
</div>
<form:form method="POST" action="/get" modelAttribute="newBook">
<div class="form-group">
<label for="authorInput">Author</label>
<form:input path="author" cssClass="form-control" id="authorInput"></form:input>
</div>
<div class="form-group">
<label for="dateInput">Date</label>
<form:input path="date" cssClass="form-control" id="dateInput"></form:input>
</div>
<button type="submit" class="btn btn-primary">Get Book</button>
</form:form>
<button type="submit" class="btn btn-primary">Add Book</button>
<button type="submit" class="btn btn-primary">Update Book</button>
</body>
Here is the controller class:
#Controller
public class MainController {
#GetMapping(value = "/")
public String welcome(Map<String, Object> model) {
model.put("newBook", new Book());
model.put("updateBook", new Book());
model.put("addBook",new Book());
return "home";
}
#PostMapping(value = "/get")
public String change(#RequestParam("author") String author, Model model,
#ModelAttribute("newBook")Book book) {
System.out.println(author);
Book b = BookDao.getBook(book.getAuthor(), book.getDate());
if(b == null){
return "home";
}
model.addAttribute("book", b);
model.addAttribute("newBook", new Book());
return "home";
}
#RequestMapping(value = "/add")
public String addBook(#RequestParam("author") String author, #RequestParam("isbn") int isbn, Model model,
#ModelAttribute("addBook") Book book){
System.out.println("Author: "+author + " ISBN: "+isbn);
model.addAttribute("addBook", new Book());
Book b= new Book(book.getName(), author,isbn, book.getDate());
model.addAttribute("add", book);
boolean result = BookDao.addBook(b);
if(result)
return "home";
else
return "error";
}
#RequestMapping( value = "/update")
public String updateBook(#RequestParam("author") String author, #RequestParam("isbn") int isbn, Model model,
#ModelAttribute("updateBook") Book book){
System.out.println("Author: "+author + " ISBN: "+isbn);
Book b= new Book(book.getName(), author,isbn, book.getDate());
model.addAttribute("updateBook", new Book());
model.addAttribute("update",b);
BookDao.updateBook(isbn, b);
return "home";
}
}
And here are the other two jsp pages:
Add.jsp:
<body>
<h1>Add a Book</h1>
<form:form method="POST" action="/add" modelAttribute="addBook">
<div class="form-group">
<label for="nameInput">Name</label>
<form:input path="name" cssClass="form-control" id="nameInput"></form:input>
</div>
<div class="form-group">
<label for="authorInput">Author</label>
<form:input path="author" cssClass="form-control" id="authorInput"></form:input>
</div>
<div class="form-group">
<label for="isbnInput">ISBN</label>
<form:input path="isbn" cssClass="form-control" id="isbnInput"></form:input>
</div>
<div class="form-group">
<label for="dateInput">Date</label>
<form:input path="date" cssClass="form-control" id="dateInput"></form:input>
</div>
<button type="submit" class="btn btn-primary">Add</button>
</form:form>
</body>
Update Book JSP Page:
<body>
<form:form method="POST" action="/update" modelAttribute="third">
<div class="form-group">
<label for="authorInput">ISBN</label>
<form:input path="isbn" cssClass="form-control" id="authorInput"></form:input>
</div>
<div class="form-group">
<label for="nameInput">Name</label>
<form:input path="name" cssClass="form-control" id="nameInput"></form:input>
</div>
<div class="form-group">
<label for="authorInput">Author</label>
<form:input path="author" cssClass="form-control" id="authorInput"></form:input>
</div>
<div class="form-group">
<label for="dateInput">Date</label>
<form:input path="date" cssClass="form-control" id="dateInput"></form:input>
</div>
<button type="submit" class="btn btn-primary">Update Book</button>
</form:form>
</body>
The problem is that the lines modelAttribute="addBook" and modelAttribute="third" in the add.jsp page and update.jsp page throw an error. The IDE says "Cannot resolve symbol 'addBook/third'". Those values are available in the home.jsp page though.
Since I found the answer, I will post it just in case somebody else gets stuck with it.
In order to access the forms on another JSP page, we can't just directly redirect to the page in an MVC design.
In order to do so, we need to create a #GetMapping annotation method for each of those JSP forms #PostMapping annotations and then redirect to the #GetMapping methods first and the #GetMapping will redirect to the JSP page. This is how it should be done:
Controller Class:
#Controller
public class MainController {
#GetMapping(value = "/")
public String welcome(Map<String, Object> model) {
model.put("newBook", new Book());
return "home";
}
#PostMapping(value = "/get")
public String change(#RequestParam("author") String author, Model model,
#ModelAttribute("newBook")Book book) {
System.out.println(author);
Book b = BookDao.getBook(book.getAuthor(), book.getDate());
if(b == null){
return "home";
}
model.addAttribute("book", b);
model.addAttribute("newBook", new Book());
return "home";
}
#GetMapping("/add")
public String show(Model model) {
model.addAttribute("addBook", new Book());
return "add";
}
#PostMapping(value = "/add")
public String addBook(#RequestParam("author") String author, #RequestParam("isbn") int isbn, Model model,
#ModelAttribute("addBook") Book book){
System.out.println("Author: "+author + " ISBN: "+isbn);
model.addAttribute("addBook", new Book());
Book b= new Book(book.getName(), author,isbn, book.getDate());
model.addAttribute("add", book);
boolean result = BookDao.addBook(b);
if(result)
return "home";
else
return "error";
}
#GetMapping("/update")
public String showUpdate(Model model) {
model.addAttribute("updateBook", new Book());
return "update";
}
#PostMapping( value = "/update")
public String updateBook(#RequestParam("author") String author, #RequestParam("isbn") int isbn, Model model,
#ModelAttribute("updateBook") Book book){
System.out.println("Author: "+author + " ISBN: "+isbn);
Book b= new Book(book.getName(), author,isbn, book.getDate());
model.addAttribute("updateBook", new Book());
model.addAttribute("update",b);
BookDao.updateBook(isbn, b);
return "home";
}
And the JSP page for Home.jsp page should be as follows:
<form:form method="POST" action="/get" modelAttribute="newBook">
<div class="form-group">
<label for="authorInput">Author</label>
<form:input path="author" cssClass="form-control" id="authorInput"></form:input>
</div>
<div class="form-group">
<label for="dateInput">Date</label>
<form:input path="date" cssClass="form-control" id="dateInput"></form:input>
</div>
<button type="submit" class="btn btn-primary">Get Book</button>
</form:form>
<button type="submit" class="btn btn-primary">Add Book</button>
<button type="submit" class="btn btn-primary">Update Book</button>
</body>
So the href maps to the #GetMapping method to get the ModelAttributes. The other JSP pages are fine as it is.
Also, another good practice I might suggest is to Use a Service layer/package to perform all the operations. So instead of performing it in the Controller, we delegate tasks like the CRUD operations to the Service layer which in turn interacts with the DAO layer.
I am creating an Spring Boot App, in which the user can create licences. The user can type the name, purchaseDate, renewalDate and expirationDate.
My problem is, that when I try to save the data, it returns null for the name, the purchaseDate, renewalDate and expirationDate.
I absolutely don't know what to do anymore, please help. The "// Comment" values in the Model:Licence are those who get null when executing the post.
Model: Licence
#Entity
#Table(name = "TBL_LICENCES")
public class Licence {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String licenceName; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String purchaseDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String renewalDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String expirationDate; // --> null after the post
public Licence() {}
public Licence(Long id, String licenceName, String purchaseDate, String renewalDate, String expirationDate) {
Id = id;
this.licenceName = licenceName;
this.purchaseDate = purchaseDate;
this.renewalDate = renewalDate;
this.expirationDate = expirationDate;
}
// Getter and Setter
}
Model: LicenceRepository
#Repository
public interface LicenceRepository extends CrudRepository<Licence, Long> { }
Service: LicenceService
#Service
public class LicenceService {
#Autowired
private LicenceRepository licenceRepository;
public List<Licence> getLicences() {
return (List<Licence>) licenceRepository.findAll();
}
public Optional<Licence> getLicenceById(Long id) {
return licenceRepository.findById(id);
}
public void addLicence(Licence licence) {
licenceRepository.save(licence);
}
public void updateLicence(Licence licence) {
licenceRepository.save(licence);
}
public void deleteLicenceById(Long id) {
licenceRepository.deleteById(id);
}
}
Controller: LicenceController
#Controller
public class LicenceController {
#Autowired
private LicenceService licenceService;
#GetMapping("/licences")
public String getLicences(Model model) {
model.addAttribute("licences", licenceService.getLicences());
return "licences";
}
#GetMapping("/onelicence")
#ResponseBody
public Optional<Licence> getLicenceByID(Long id, Model model) {
model.addAttribute("onelicence", licenceService.getLicenceById(id));
return licenceService.getLicenceById(id);
}
#RequestMapping(value="/save", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String updateLicence(Licence licence) {
licenceService.updateLicence(licence);
return "redirect:/licences";
}
**// Is performed, when clicking "New Licence > Save"**
#RequestMapping(value="/addNew", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String addLicence(Licence licence) {
licenceService.addLicence(licence);
return "redirect:/licences";
}
#RequestMapping(value="/delete", method = {RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET})
public String deleteLicence(Long id) {
licenceService.deleteLicenceById(id);
return "redirect:/licences";
}
}
licences.html
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script type ="text/javascript" src="webjars/jquery/3.4.1/jquery.min.js"></script>
<script type ="text/javascript" src="webjars/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link href="webjars/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
<title>Licence</title>
</head>
<body>
<div class = "container">
<h2>Licences</h2>
<table class = "table table-striped">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Kaufdatum</td>
<td>Erneuerungsdatum:</td>
<td>Auslaufdatum:</td>
</tr>
</thead>
<tbody>
<tr th:each = "licence: ${licences}">
<td th:text="${licence.id}">ID</td>
<td th:text="${licence.licenceName}">Name</td>
<td th:text="${licence.purchaseDate}">Kaufdatum</td>
<td th:text="${licence.renewalDate}">Erneuerungsdatum</td>
<td th:text="${licence.expirationDate}">Auslaufdatum</td>
<td><a class="btn btn-warning">Edit</a></td>
<td><a class="btn btn-danger">Delete</a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addModal" data-whatever="#mdo">New Licence</button>
</div>
// Bootstrap Varying Modal Content
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form th:action="#{/addNew}" method ="post">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New Licence</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-form-label">Lizenz Name</label>
<input type="text" class="form-control" id="licenceName" name="name">
</div>
<div class="form-group">
<label for="purchase" class="col-form-label">purchaseDate</label>
<input type="date" class="form-control" id="purchaseDate" name="purchase">
</div>
<div class="form-group">
<label for="renewalAdd" class="col-form-label">renewalDate</label>
<input type="date" class="form-control" id="renewalAdd" name="renewal">
</div>
<div class="form-group">
<label for="expirationAdd" class="col-form-label">expirationDate</label>
<input type="date" class="form-control" id="expirationAdd" name="expiration">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
</body>
</html>
schema.sql
DROP TABLE IF EXISTS TBL_LICENCES;
CREATE TABLE TBL_LICENCES (
id INT AUTO_INCREMENT PRIMARY KEY,
licence_name VARCHAR(250),
purchase_date VARCHAR(250),
renewal_date VARCHAR(250),
expiration_date VARCHAR(250)
);
data.sql --> This works and the data is shown.
INSERT INTO
TBL_LICENCES (licence_name, purchase_date, renewal_date, expiration_date)
VALUES
('Test1', '2020-01-31', '2020-06-31', '2020-12-31'),
('Test', '2021-01-31', '2021-06-31', '2021-12-31');
Properties: application.properties
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:<dbLicences>
spring.jpa.hibernate.ddl-auto=update
The name attribute of the input control should match the name of the fields in the Licence class. Currently your id attribute is matching the name of the fields, but when a form is submitted it uses the name attribute to build the request parameters.
Update your HTML to match something like:
<input type="text" class="form-control" id="licenceName" name="licenceName" />
Fix the name for other fields and you will have the Licence object populated with data from the form.
Also, I see that
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String licenceName;
licenceName is annotated with #DateTimeFormat. I guess this is a mistake, please correct that as well.
I rewrite code from Mastering Spring book's and it does not work because all time I got the exception:
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'profileForm' available as request attribute
My form:
<form th:action="#{/profile}" th:object="${profileForm}" method="post" class="col m8 s12 offset-m2">
<div class="row">
<div class="input-field col s6">
<input th:field="${profileForm.twitterHandle}" id="twitterHandle" type="text"/>
<label for="twitterHandle" th:text="#{twitter.handle}">Identyfikator Twitter</label>
</div>
<div class="input-field col s6">
<input th:field="${profileForm.email}" id="email" type="email"/>
<label for="email">Adres e-mail</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input th:field="${profileForm.birthDate}" id="birthDate" type="text"/>
<label for="birthDate" th:text="#{birthdate}">Data urodzenia</label>
</div>
</div>
<div class="row s12 center">
<button class="btn indigo waves-effect waves-light" type="submit" name="save">Wyślij
<i class="mdi-content-send right"></i>
</button>
</div>
</form>
My POJO:
package masterspringmvc.profile;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class ProfileForm {
private String twitterHandle;
private String email;
private LocalDate birthDate;
private List<String> tastes = new ArrayList<>();
// getters setters
}
My Controller:
#Controller
public class ProfileController {
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public String displayProfile() {
return "profile/profilePage";
}
#RequestMapping(value = "/profile", method = RequestMethod.POST)
public String saveProfile(ProfileForm profileForm) {
System.out.println("Profil: " + profileForm);
return "redirect:/profile";
}
}
Tomcat prints shows:
Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (profile/profilePage:16)
According to book everything should be ok, but I still getting errors, why?
I think you need to:
Add "profileForm" to the model.
Add "#ModelAttribute("profileForm")" to the post controller.
In addition, you can simplify your #RequestMappings
#Controller
public class ProfileController {
#GetMapping("/profile")
public String displayProfile(Map<String, Object> model) {
model.put("profileForm", new ProfileForm());
return "profile/profilePage";
}
#PostMapping("/profile")
public String saveProfile(#ModelAttribute("profileForm") ProfileForm profileForm) {
System.out.println("Profil: " + profileForm);
return "redirect:/profile";
}
}
I'm trying to have Spring boot convert my form data into an Object representation (ExampleInputForm), but for some reason it doesn't seem to bind (emailName is always null). Am I missing something here?
ExampleInputForm.java
public class ExampleInputForm {
#NotNull
public String emailName;
public ExampleInputForm() {
super();
}
#Override
public String toString() {
return "ExampleInputForm{" +
"emailName='" + emailName + '\'' +
'}';
}
}
MyController.java
#Controller
class MyController {
#RequestMapping(method = RequestMethod.POST, value="/save")
#ResponseBody
public Map<String, String> saveBrands(#Valid ExampleInputForm form, BindingResult bindingResult) {
LOG.info("Saving brands: " + form);
return ImmutableMap.of(
"emailName", form.emailName,
);
}
}
form.ftl
<div class="container">
<div class="row text-center">
<form name="input" action="/save" method="post">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email" name="emailName">
</div>
<input type="submit" value="Save" class="btn btn-default" />
</form>
</div>
</div>
You need to add getter/setter for your emailName attribute to your ExampleInputForm class.
When i edit an entity i want to set the selected value for the category in the drop down list. But i am not able to do that the way i tried. I am new to spring mvc but i am sure that there is a better way to map a model to a view. Can you provide some examples?
editMeal.jsp
<%# include file="/WEB-INF/template/taglibs.jsp"%>
<div class="container">
<%# include file="menu.jsp"%>
<form:form commandName="editForm" method="post"
class="form-horizontal form-width">
<fieldset>
<legend>Edit meal</legend>
<div class="form-group">
<input class="hidden" type="text" value="${idmeal}" hidden="true"
name="idmeal" />
<label for="name" class="col-lg-2 control-label">Name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="name"
placeholder="Name" name="name" value="${name }">
</div>
</div>
<div class="form-group">
<label for="select" class="col-lg-2 control-label">Category</label>
<div class="col-lg-10">
<select name="idCategory" class="form-control">
<option value="NONE">Select category</option>
<c:forEach items="${categories}" var="curCategory">
<c:choose>
<c:when test="${curCategory eq category}">
<option value= "${category.text}" selected="selected">${category.value}</option>
</c:when>
<c:otherwise>
<option value="${category.text}">${category.value}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="shortName" class="col-lg-2 control-label">Short name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="shortName"
placeholder="Short name" name="shortName" value="${shortName }">
</div>
</div>
<div class="form-group">
<label for="description" class="col-lg-2 control-label">Description</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="description"
placeholder="Description" name="description" value="${description }">
</div>
</div>
<div class="form-group">
<label for="price" class="col-lg-2 control-label">Price</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="price"
placeholder="Price" name="price" value="${price }">
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<a class="btn btn-warning"
href="http://localhost:8080/Catering/index/meals">Cancel</a>
<button type="submit" class="btn btn-warning">Submit</button>
</div>
</div>
</fieldset>
</form:form>
</div>
DropDownListItem.java
public class DropDownListItem {
private String text;
private String value;
public DropDownListItem(String t, String n) {
text = t;
value = n;
}
//getters and setters
}
MealController.java
package catering.web.controller;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import catering.web.data_access.CategoryDataAccess;
import catering.web.data_access.MealDataAccess;
import catering.web.data_access.MealSummaryDataAccess;
import catering.web.helper.DropDownListItem;
import catering.web.mapper.MealMapper;
import catering.web.model.Category;
import catering.web.model.Meal;
import catering.web.model.MealSummary;
import catering.web.view_model.MealViewModel;
#Controller
public class MealController {
protected static Logger logger = Logger.getLogger("controller");
#RequestMapping(value = "meals", method = RequestMethod.GET)
public String mealsPage(Model model, Authentication authentication){
logger.debug("Received request to show meals page(GET)");
List<MealSummary> meals = MealSummaryDataAccess.getMeals();
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
model.addAttribute("list", meals);
return "meals";
}
#RequestMapping(value = "addMeal", method = RequestMethod.GET)
public String addMealGet(Model model, Authentication authentication){
populateModel(model);
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
model.addAttribute("addForm", new MealViewModel());
return "addmeal";
}
#RequestMapping(value = "addMeal", method = RequestMethod.POST)
public String addMealPost(#ModelAttribute("addForm") MealViewModel mealVM, Model model, Authentication authentication){
populateModel(model);
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
Meal meal = MealMapper.mapMealVMToMeal(mealVM);
MealDataAccess.insertMeal(meal);
return "redirect:meals";
}
#RequestMapping(value = "editMeal", method = RequestMethod.GET)
public String editMealGet(#RequestParam int id, Model model, Authentication authentication) {
logger.debug("Received request to show edit user page(GET)");
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
populateModel(model);
Meal meal = MealDataAccess.getMealById(id);
MealViewModel mealMV = MealMapper.mapMealToMealVM(meal);
model.addAttribute("idmeal", mealMV.getIdMeal() );
model.addAttribute("name",mealMV.getName());
model.addAttribute("category",mealMV.getIdCategory());
model.addAttribute("shortName", mealMV.getShortName() );
model.addAttribute("description", mealMV.getDescription() );
model.addAttribute("price", mealMV.getPrice() );
return "editmeal";
}
#RequestMapping(value = "deleteMeal", method = RequestMethod.POST)
public String deleteMeal(#ModelAttribute("delete") #RequestParam int id){
logger.debug("Received request to delete meal page(POST)");
MealDataAccess.deleteMeal(id);
return "redirect:meals";
}
private void populateModel(Model model){
List<Category> cats = CategoryDataAccess.getCategories();
List<DropDownListItem> categories = new ArrayList<DropDownListItem>();
for(Category cat : cats){
categories.add(new DropDownListItem(Integer.toString(cat.getIdCategory()),cat.getName()));
}
model.addAttribute("categories", categories);
}
}
MealViewModel.java
public class MealViewModel {
private int idMeal;
private String name;
private String shortName;
private String description;
private String idCategory;
private float price;
//getters and setters
}
You will have to use the following tag library for this:
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
Write the select tag as follows:
<form:form commandName="editMeal" method="post"
class="form-horizontal form-width">
<-- other code -->
<form:select id="categoryList" path="idCategory">
<c:forEach items="${categories}" var="cat">
<form:option value="${category.text}" label="${category.value}" />
</c:forEach>
</form:select>
</form:form>
The path will be the property in the model attribute editMeal that is to be used to set the value in the list.
Set the editMeal attribute in the controller as follows:
model.addAttribute("editMeal", mealMV);
You can try it out with spring spring:bind tag as,
<spring:bind path="idCategory">
<select name="idCategory" class="form-control">
<option value="NONE">Select category</option>
<c:forEach items="${categories}" var="curCategory">
<c:choose>
<c:when test="${curCategory eq modelAttribute.idCategory}">
<option value= "${curCategory}" selected="true">${curCategory}</option>
</c:when>
<c:otherwise>
<option value="${curCategory}">${curCategory}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</spring:bind>
Hope this helps !!