Spring Request method 'POST' not supported with th:field in select - spring

I have strange problem with post method from form to method in controller.
Every mapping is ok but when i put th:field in 'select' I receive an error 405.
I tried remove this (th:field=*{code}) and then it's working but i dont have value of this.
So, in my controller in GET method i put to the model list of Code objects and new Position of object (for form)
Then in form html in "select" i display every single Code object.
In short, i would like to post data from select to the post method in my controller
Thats my code in controller:
#GetMapping("/add-shop-position")
public ModelAndView addShopPositionForm(){
ModelAndView modelAndView = new ModelAndView("admin/addShopPosition");
List<Code> codes = codeService.getAllCodes();
modelAndView.addObject("codes", codes);
modelAndView.addObject("position", new Position());
return modelAndView;
}
#PostMapping("/add-shop-position")
public ModelAndView addShopPositionSubmit(#ModelAttribute Position position){
ModelAndView modelAndView = new ModelAndView("admin/addShopPosition");
if(positionService.addPosition(position)){
//I will do something here in future
}else{
//I will do something here in future
}
return modelAndView;
}
And of course my controller has RequestMapping:
#Controller
#RequestMapping(value = "/admin")
public class AdminController {}
And thats my form, html view in Thymeleaf:
<form action="#" th:action="#{/admin/add-shop-position}" th:object="${position}" method="POST"
style="margin: 0 auto; width: 50%">
<div class="form-group">
<input class="form-control" type="text" th:field="*{title}" th:placeholder="Title">
</div>
<div class="form-group">
<input class="form-control" type="text" th:field="*{description}" th:placeholder="Description">
</div>
<div class="form-group">
<select class="form-control" th:field="*{code}">
<option th:each="code : ${codes}"
th:value="${code}"
th:text="${code.getCode()}"></option>
</select>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit" value="Submit">Create</button>
</div>
</form>

Related

Thymleaf controller request mapping binging issue

I am new to thymleaf and at first i used simple requestmapping and thymleaf th:action and th:object to bind controller methods. But after adding class level requestmapping i cannot view my html.
below is my controller class code where i redirect to login page.
#Controller
#RequestMapping("/project")
public class MyController {
#RequestMapping(value = {"/"}, method = RequestMethod.GET)
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}
}
below is my html page.
<form class="user" th:action="#{/project/login}" th:object="${mylogin}" method="POST">
<div class="form-group">
<input type="email" id="user_name" name="username"
class="form-control form-control-user"
placeholder="Enter Email Address..." />
</div>
<div class="form-group">
<input type="password" id="password" name="password"
class="form-control form-control-user"
placeholder="Password" />
</div>
<button class="btn btn-primary btn-user btn-block"
name="Submit" value="Login" type="Submit" th:text="Login"></button>
</form>
after adding #RequestMapping("/project") in class i cannot fetch html. If i remove this #RequestMapping("/project") and change th:action="#{/project/login}" to th:action="#{/login}" my code works.
What can be the problem for such issue?
Change This Request mapping this way /login with class level requestmapping and try again:
#RequestMapping(value = {"/login"}, method = RequestMethod.GET)
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}
OR
#GetMapping("/login")
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}

form th:action not working using thymeleaf and spring

i am new in spring and thymeleaf, i am trying to submit a form, insert to database, but whe i am using form submission with submission it simply redirects to page and don't invoke to controller
i don't know why,
please, help
here is my Controller
#Controller
public class AdminController {
#Autowired
private CategoryServiceImpl categoryService;
#GetMapping("/adminPage")
public String index(){
return "adminPage";
}
#GetMapping("/categoryList")
public String showCategory(){
return "categoryList";
}
#GetMapping("form")
public String categoryForm(Model model, Category category){
model.addAttribute("category", category);
// model.addAttribute("add", true);
// categoryService.create(category);
return "admin/categoryForm";
}
#PostMapping("create")
public String addOrgCategory(#Valid Category orgCategory) {
categoryService.create(orgCategory);
return "redirect:/categoryList";
}
my html form is here
<form action="#" th:action="#{/create}" th:object="${category}" method="POST">
<div class="form-group">
<label for="name" class="text-dark font-bold">Category name</label>
<input id="name" type="text" class="form-control" th:value="${category} ? ${category.name} : ' '" th:field="*{name}">
</div>
<div class="form-group">
</div>
<button
type="submit" class="btn btn-success" data-toggle="tooltip"
data-placement="top" title="Tooltip on top">Create
</button>
</form>

form:error not showing error

When validating object controller just refreshes the page, instead showing errors via form:errors, can you tell me where is the problem. I guest it needs to get error from binding result and insert it into the page, instead controller is just refreshing page
Controller :
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createProduct(MultipartFile image, Model model,#ModelAttribute #Valid ProductDto product, BindingResult bindingResult) throws IOException {
productValidator.validate(product,bindingResult);
if (bindingResult.hasErrors()){
return "admin/create";
} else {
return "index";
}
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String createProduct(Model model) {
model.addAttribute("product", new ProductDto());
model.addAttribute("categories", categoryService.findAll());
return "admin/create";
}
Validator :
#Override
public boolean supports(Class<?> aClass) {
return ProductDto.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
ProductDto product = (ProductDto) o;
if (product.getTitle().isEmpty() || product.getTitle() == null) {
errors.rejectValue("title", "product.title", "Product title cant be empty");
}
if (product.getDescription().isEmpty() || product.getDescription() == null) {
errors.rejectValue("description", "product.description", "Product description cant be empty");
}
if (product.getPrice() == null || product.getPrice()<=0) {
errors.rejectValue("price", "product.price", "Product price is not valid");
}
if (product.getCategoryId()==null) {
errors.rejectValue("category", "product.category", "Product category is not valid");
}
}
jstl page :
<spring:url value="/product/create" var="formUrl"/>
<form:form modelAttribute="product" action="${formUrl }" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<form:input id="title"
cssClass="form-control" path="title"/>
<form:errors path="title"/>
</div>
<div class="form-group">
<label>Description</label>
<form:textarea id="description" rows="10"
cssClass="form-control" path="description"/>
</div>
<div class="form-group">
<label>Price</label>
<form:input id="price" type="number"
cssClass="form-control" path="price"/>
<form:errors path="description"/>
</div>
<div class="form-group">
<label for="sel1">Select category</label>
<form:select id="sel1" cssClass="form-control" path="categoryId">
<form:options items="${categories}" itemValue="id" itemLabel="title"/>
</form:select>
</div>
<label class="btn btn-default btn-file">
Image <input type="file" multiple accept='image/*' ng-file-select="onFileSelect($files)" name="image" style="display: block;">
</label>
<br><br>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-success text-center"><span
class="fa fa-check"></span> Submit
</button>
<a href="/" class="btn btn-danger btn-lg text-center"><span
class="fa fa-times"></span> Cancel
</a>
</div>
</form:form>
I think your product model attribute has different name in your POST-handling method.
According to documentation:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List<mypackage.OrderAddress>".
So your ProductDto product attribute will have name productDto in resulting model. But you referring the product in your template.
Try to explicitly set the name attribute of #ModelAttribute annotation
public String createProduct(MultipartFile image, Model model, #ModelAttribute(name = "product") #Valid ProductDto product, BindingResult bindingResult)
And to make sure that bidning and validation actually works, try to log it:
if (bindingResult.hasErrors()) {
System.err.println("Form has errors!");
}

Returning form variables in same JSP as form in Spring MVC

I'm working on a simple web app and part of the part is a search. Currently I'm just trying to return the form variables from the search, and display them on the search page once the user hits submit.
HTML:
<form:form modelAttribute="Search" action="performSearch" method="post" class="form-horizontal">
<fieldset>
<div class="form-bottom">
<div class="form-group">
<select class="form-control" id="profession" name="profession">
<option value="General Contractor">General Contractor</option>
<option value="Plumber">Plumber</option>
</select>
</div>
<div class="form-group">
<form:input path="zipcode" type="text" name="form-zip" placeholder="Zipcode..." class="form-control"></form:input>
</div>
<div class="input-group">
<span class="input-group-addon">$</span>
<form:input path="cost" type="text" name="form-cost" placeholder="Max Cost per Hour..." class="form-control"></form:input>
</div>
<br>
<input type="submit" class="btn btn-default custom_btn" value="Search">
</div>
</fieldset>
</form:form>
HTML to Display after searching.
<c:if test="${not empty test1}">
test1
</c:if>
<c:if test="${not empty test2}">
test2
</c:if>
<c:if test="${not empty test2}">
test3
</c:if>
Controller for search.jsp.
#RequestMapping(value="/performSearch")
public ModelAndView performSearch(#ModelAttribute("search")Search search, ModelMap model) {
model.addAttribute("test1",search.getCost());
model.addAttribute("test2",search.getProfession());
model.addAttribute("test2",search.getZipcode());
return new ModelAndView("search");
}
I think at this point I'm just having trouble figuring out what to return. Do I want to return a ModelAndView?
Edit:
So I forgot to return the model and model object. In this case,
return new ModelAndView("search", "Search", new Search());
Search being an object I created to hold the variables.
Yes the model and view object will take the name of the view passed in the constructor. In this case a JSP. The view resolver will wire this together for us. The model content will be made available in the JSP. In your case search.jsp. the model and view object is just a handy way of combining the model data with the view. You can also create them them different ways. Your on the right path.
I create the model and view this way
ModelAndView mav = new ModelAndView("someview");
mav.addObject("someobject", myObject);
return mav;
You can then access someobject implicitly from someview.jsp using EL

spring RequestMapping not working without ModelAttribute

I have controller class with following request mapping method.
appStart() method is responsible for redirecting user to login.html and
logout() is responsible for invalidating session and redirecting user
back to login.jsp
if I remove #ModelAttribute from their parameter then these two methods are throwing exception, is there any hack to get these methods working without modelattribute?
controller methods.
#RequestMapping(value="/",method=RequestMethod.GET)
public String appStart(#ModelAttribute("tempAdmin") Admin tempAdmin) {
return "login.jsp";
}
#RequestMapping(method = RequestMethod.POST,name="doLogin")
public ModelAndView doLogin(#ModelAttribute("tempAdmin") Admin tempAdmin, HttpServletRequest request) {
ModelAndView mvc = new ModelAndView();
/*
Buisness logic
*/
mvc.setViewName("home.jsp");
return mvc;
}
#RequestMapping("doLogout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session != null){
session.invalidate();
}
return "login.jsp";
}
login.jsp
<form:form action="doLogin" modelAttribute="tempAdmin" cssClass="form-horizontal">
<div class="form-group">
<label for="username" class="col-sm-2 control-label">Username</label>
<div class="col-sm-10">
<form:input cssClass="form-control" path="adminId" placeholder="username" />
</div>
</div>
<div class="form-group">
<label for="passwd" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<form:password path="password" cssClass="form-control" id="passwd" placeholder="password" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form:form>
stacktrace.
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'tempAdmin' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
I will tell you how to change your controller, to avoid binding result problem. Try this :
#RequestMapping(method = RequestMethod.POST,name="doLogin")
public String doLogin(#ModelAttribute("tempAdmin") Admin tempAdmin, HttpServletRequest request,Model model) {
model.addAttribute("tempadmin",new Admin());
// business logic
return "home";
}
Try this out, and if you have any other classes, then add the model.addAttribute for that as well. Can you post your JSP too?

Resources