I need sone help to send the List produtos as parameter to my controller.
<div class="panel-body">
<div th:each="produto : ${produtos}" class="form-group" id="produtos">
<label th:text="${produto.nome}" class="col-sm-2 control-label">
</label>
<div class="col-sm-1">
<input type="text" class="form-control js-number" id="quantPedido"
th:field="*{produto.quantPedido}"/>
</div>
</div>
</div>
My Controller
#RequestMapping(method = RequestMethod.POST)
public String salvar(#Validated Pedido pedido, List<Produto> produtos, Errors errors, RedirectAttributes attributes) {
}
Thanks so Much!
You need to warp yours products list into some bean, then send that bean to the html and fill it. Example:
Bean:
public class Pedido{
#Getter #Setter
private List<Product> products = new ArrayList();
//extra attributes
}
In your controller:
ModelAndView mav = new ModelAndView("yourView");
Pedido pedido = new Pedido();
mav.addObject("pedido", pedido);
In your html:
<form th:object="${pedido}" th:action="#{/savePedido}" method="POST">
//...
<div th:each="produto : ${produtos}" class="form-group" id="produtos">
<label th:text="${produto.nome}" class="col-sm-2 control-label">
</label>
<div class="col-sm-1">
<input class="form-control js-number" id="quantPedido" th:field="*{products[__${produtoStat.index}__].quantPedido}"/>
</div>
</div>
//...
</form>
Related
I'm trying to implement a login form in a Spring boot application. It has an email and a password field. The email field failed to get user input, here is the form:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" name="q" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
Here is the controller:
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name = "q", required = false) Optional<String> email) {
Optional<UserDto> aUser;
System.out.println(email);
if (email.isPresent()) {
aUser = userService.getAUserByEmail(email.get());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
I thought the #RequestParam(name = "q") and name="q" in html would do the job, but I always get Optional.empty for email. Any idea what's wrong here?
UPDATE:
From the answers I changed controller to this:
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
Optional<UserDto> aUser;
if (loginForm.getEmail() != null) {
aUser = userService.getAUserByEmail(loginForm.getEmail());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
login-form.html to this:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
I also have LoginForm.java like this
#Data
#AllArgsConstructor
#NoArgsConstructor
public class LoginForm {
private String email;
private String password;
}
but still not getting user email field input?
The way you have set up your form, it's mapping the value of your email input field to the property email (that's what th:field="*{email}" means) of an object called loginForm (that's what th:object="${loginForm}" means). Neither of these seem to be used or even exist in your login() method. You need to either change what you use in your controller to match what you have in your Thymeleaf template, or change your Thymeleaf template to actually reference what you are using in your controller.
The problem in your code is located under th:object="${loginForm}"
With this you inform spring to bind the data sent from the form into an object named loginForm.
So Spring actually expects the controller to be
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
....
and inside LoginForm a field named email will contain the value sent from the form, as you have declared with <input type="text" th:field="*{email}" .... >
If you don't want the data to be bound into an object from Spring Mvc then
remove the th:object="${loginForm}"
use the
<input type="text" th:name="q" class="form-control" required />
and then the controller will receive the sent value as a query parameter
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name =
"q", required = false) Optional<String> email) {
I'm sort-of shocked that I can't find an example of how to do this. Every time I google it, I get info on how to post a collection of objects, or other unrelated stuff. The thymeleaf documentation (what I can find of it) seems to not explain much either, like there is a lot of assumed knowledge.
Getting back to my question, I just want to post a single object (bean) from a form. I would like my controller mapping method to bind to this "pojo" bean and not to a bunch of strings/integers.
The only thing that I have found that comes close is stuff on StackOverflow where half of the code is in the question, the other half is in the answer, and there are always a few comments from people saying it didn't work for them.
Can anyone offer any relief here with a plain old boring example?
Can find the below code snippet might helpful for you.
Controller GET/POST mapping:
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registartionPage(Model model) {
Registration registration = new Registration();
model.addAttribute("registration", registration);
return "registarion/registarion";
}
#RequestMapping(value = "/user/new-user-registrn", method = RequestMethod.POST)
public String newUserRegistrn(Model model, #ModelAttribute("registration")
Registration registration, RedirectAttributes redirectAttributes) {
try {
StarUser user = starSecurityService.findSysUserName(registration.getUserName());
if (user != null) {
throw new Exception("User Already Exist. Please try with different User Name");
}
user = (StarUser) starUtilService.save(setStarUser(registration));
model.addAttribute("registration", registration);
if (user != null) {
redirectAttributes.addAttribute("starMessage",
"Your Account is successfully created !! Login to Access the Application");
return "redirect:/";
}
} catch (Exception e) {
model.addAttribute(STAR_MESSAGE, e.getMessage());
}
return "registarion/registarion";
}
Thymeleaf Content:
<form class="form-horizontal col-sm-12" method="POST" th:action="#{/user/new-user-registrn}" th:object="${registration}">
<div class="row">
<div class="form-group col-md-12">
<div class="star-reg-header">New User Registration</div>
</div>
<div class="star-reg-body">
<div class="form-group col-sm-4">
<label class="required">First Name: </label>
<input type="text" class="form-control required" th:field="*{firstName}" required="required" />
</div>
<div class="form-group col-sm-4">
<label class="required">Last Name: </label>
<input type="text" class="form-control" th:field="*{lastName}" required="required" />
</div>
<div class="form-group col-sm-4">
<label class="required">User Name: </label>
<input type="text" class="form-control" th:field="*{userName}" required="required" />
</div>
<div class="form-group col-sm-4">
<label class="required">Password: </label>
<input type="password" class="form-control" th:field="*{password}" required="required" />
</div>
<div class="form-group col-sm-4">
<label class="required">Email: </label>
<input type="text" class="form-control" th:field="*{email}" required="required" />
</div>
</div>
</div>
<div class="form-group col-md-12">
<label class="col-sm-2"></label>
<div class="col-sm-10">
<button type="submit" class="btn btn-info">Submit</button>
</div>
</div>
Java Bean class
public class Registration {
protected String firstName;
protected String lastName;
protected String userName;
protected String password;
protected String email;
//Setter and Getter
}
Use #ModelAttribute annotation in the parameter.
Something like this.
#RequestMapping(value = "/someurl", method = RequestMethod.POST)
public String savePojo(#ModelAttribute PojoClass pojo, Model model) {
//Code
}
Edit: This answer has very good info on this.
What is #ModelAttribute in Spring MVC?
I am trying to retrieve object on my controller that mapped to my POJO
My POJO looks like this
public interface InventoryDetailPOJO {
String getItem_cd();
}
And this is my form
<div class="row d-flex">
<div class="p-3">
<button class="btn btn-block btn-options btn-save">Save</button>
</div>
</div>
<form action="#"
th:action="#{/stock-list/inventory-detail}"
method="post"
th:object="${inventoryDetail}"
class="pt-3 form-inventory-detail">
<div class="form-group row">
<label for="item_cd" class="col-2 col-form-label col-form-label-sm">
<span class="pull-right">Item No</span>
</label>
<div class="col-10">
<input type="text" class="form-control form-control-sm w-25"
th:field="*{item_cd}">
</div>
</div>
</form>
And for my controller
#RequestMapping(value = "/stock-list/inventory-detail", method = RequestMethod.POST)
public ModelAndView InventoryDetailSubmitPage(ModelAndView modelAndView,
#ModelAttribute("inventoryDetail") InventoryDetailPOJO inventoryDetail,
#RequestParam("item_cd") String item_cd) {
System.err.println("InventoryDetail: " + inventoryDetail);
System.err.println("item_cd: " + item_cd);
modelAndView.setViewName("redirect:/stock-list");
return modelAndView;
}
There're no item on inventoryDetail when I tried to log it, but There's a value on item_cd
Remove action="#", like this:
<form th:action="#{/stock-list/inventory-detail}"
method="post"
th:object="${inventoryDetail}"
class="pt-3 form-inventory-detail">
Hello I am developing a CMS using Spring and Hibernate. I have a page displaying existing products and a form which should add a new product to the database. However when I fill out the form and submit it nothing gets saved to the database. Additionally System.out.println does not print anything to the console so I have no idea where is the problem.
jsp page
<form:form class="form-horizontal" method="post" modelAttribute="productForm">
<div class="form-group">
<label class="col-sm-2 control-label"> Product Name:</label>
<div class="col-sm-10">
<form:input class="form-control" type="text" id="productName" name="product name" path="name" />
<br></div></div>
<div class="form-group">
<label class="col-sm-2 control-label"> Serial number:</label>
<div class="col-sm-10">
<form:input class="form-control" type="text" id="productSerial" name="serial number" path="serial" value=" " /></div></div>
<input type="submit" value="Submit">
</form:form>
controller
#RequestMapping(value = "/saveNewContact", method = RequestMethod.POST)
public ModelAndView saveContact(#ModelAttribute("userForm") Product product,
BindingResult result, Model model) {
System.out.println(product);
productService.saveOrUpdate(product);
return new ModelAndView("redirect:/");
}
Service
#Override
public void saveProduct(Product product) {
productDao.saveProduct(product);
}
DAO
the interface implementation has #Transactional(readOnly = false)
public void saveNewProduct(Product product) {
persist(product);
}
The problem was in the form just adding action="saveNewContact" fixed the issue.
Here's my controller
#RequestMapping(value = "/save",method = RequestMethod.POST)
public ModelAndView saveUserAccount(#ModelAttribute("account") UserAccountForm userAccount, BindingResult result){
AddUserValidator userValidator = new AddUserValidator();
userValidator.validate(userAccount,result);
boolean hasErrors = result.hasErrors();
if(hasErrors){
return render(ADD_USER_VIEW)
.addAttr("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()))
.addAttr("TENANTS",tenantInfoService.getAll())
.addAttr("errors",result)
.addAttr("account",userAccount).toMav();
}
return render(ADD_USER_VIEW)
.addAttr("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()))
.addAttr("TENANTS",tenantInfoService.getAll())
.addAttr("account",new UserAccountForm())
.toMav();
}
Here's the render library That I have created.
public class RenderMavBuilder {
private final ModelAndView mav;
public static RenderMavBuilder render(String viewname){
RenderMavBuilder target = new RenderMavBuilder(viewname);
return target;
}
public RenderMavBuilder addAttr(String attrName, Object value){
mav.addObject(attrName, value);
return this;
}
public RenderMavBuilder addAttr(Object value){
mav.addObject(value);
return this;
}
public RenderMavBuilder addAttrs(Map<String , ?> attrs){
mav.addAllObjects(attrs);
return this;
}
private RenderMavBuilder(String viewName){
this.mav = new ModelAndView(viewName);
}
public ModelAndView toMav(){
return mav;
}
}
Here's my validator
Here's my form.
<div class="col-md-6 centered">
<form:errors path="*" />
<form:form commandName="account" method="post" action="${pageContext.request.contextPath}/user/save">
<!-- Username -->
<div class="col-xs-12 form-group">
<label class="control-label">Username</label>
<form:input path="username" type="text" class="form-control" />
</div>
<!-- Password -->
<div class="col-xs-12 form-group">
<label class="control-label">Password</label>
<form:password path="password" class="form-control"/>
</div>
<!-- Password -->
<div class="col-xs-12 form-group">
<label class="control-label">Password</label>
<form:password path="retypedPassword" class="form-control"/>
</div>
<!-- First Name -->
<div class="col-xs-12 form-group">
<label class="control-label">First Name</label>
<form:input path="firstName" type="text" class="form-control"/>
</div>
<!-- First Name -->
<div class="col-xs-12 form-group">
<label class="control-label">Last Name</label>
<form:input path="lastName" type="text" class="form-control"/>
</div>
<!-- User Role -->
<div class="col-xs-12 form-group">
<label class="control-label">User Role</label>
<form:select path="accountRole" class="form-control">
<form:options items="${ACCOUNT_ROLES}"/>
</form:select>
</div>
<!-- Branch Designation -->
<div class="col-xs-12 form-group">
<label class="control-label">Designated Branch</label>
<select path="tenantId" items="${TENANTS}" class="form-control">
<c:forEach var="branch" items="${TENANTS}">
<option value="${branch.id}">${branch.tenantDescription}</option>
</c:forEach>
</select>
</div>
<!-- Button -->
<div class="col-md-12 form-group">
<button class="form-control btn btn-primary submit-button" type="submit">Save New User <i class="fa fa-check-square-o"></i></button>
</div>
</form:form>
On my controler the binding result has errors. however, the error is not being displayed on the view, what am I missing?
The problem is the way you are constructing your ModelAndView, you should use the Errors object to construct it. Use Errors.getModel() to obtain the underlying Map that represents the model and use that, next to the view name, to construct the ModelAndView.
Next to that for your other model attributes you should simply add #ModelAttribute annotated methods to retrieve those. I would also suggest adding a GET based method which you redirect to after successful submit.
Your helper class seems also rather pointless as doing it simply inside your controller would simplify your code (imho).
Your controller could look something along these lines.
#RequestMapping(value = "/save",method = RequestMethod.POST)
public ModelAndView saveUserAccount(#ModelAttribute("account") UserAccountForm userAccount, BindingResult result){
AddUserValidator userValidator = new AddUserValidator();
userValidator.validate(userAccount,result);
boolean hasErrors = result.hasErrors();
return new ModelAndView(hasErrors ? ADD_USER_VIEW, "redirect:/add", errors.getModel());
}
#RequestMapping(value="/add")
public String addUserAccount(Model model) {
model.addObject("account", new UserAccountForm());
return ADD_USER_VIEW;
}
#ModelAttribute
public void referenceData(Model model) {
model.addObject("ACCOUNT_ROLES", Arrays.asList(AccountRole.values()));
model.addObject("TENANTS",tenantInfoService.getAll());
}