I have an issue with form processing using Thymeleaf and Spring-MVC.
This is my view:
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container" id="containerFragment" th:fragment="containerFragment">
<form
action="#"
th:action="#{/search}"
th:object="${searchInfo}"
method="post" >
<fieldset id="search-query">
<input
type="text"
name="search"
value=""
id="search"
placeholder="Search for user"
required="required"
th:value="*{searchQuery}" />
<input
type="submit"
value="Search"
name="submit"
class="submit"/>
</fieldset>
</form>
</div>
</body>
</html>
this is my controller:
/** Search form */
#RequestMapping(value = "/search", method = RequestMethod.GET)
public String search(Model model) {
model.addAttribute("searchInfo", new SearchForm());
return "search";
}
/** Search form */
#RequestMapping(value = "/search", method = RequestMethod.POST)
public ModelAndView search(BindingResult result,
#Valid #ModelAttribute("searchInfo") SearchForm searchForm) {
String login = searchForm.getSearchQuery();
User user = userService.findUserByLogin(login);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("search-results");
modelAndView.addObject("user", user);
return modelAndView;
}
and the search form is:
public class SearchForm {
String searchQuery;
public String getSearchQuery() {
return searchQuery;
}
public void setSearchQuery(String searchQuery) {
this.searchQuery = searchQuery;
}
#Override
public String toString() {
return "SearchForm [searchQuery=" + searchQuery + "]";
}
}
The issue is that login is null at this point of controller:
String login = searchForm.getSearchQuery();
It looks like a new SearchForm object created for POST method, but there are already one, which was created at GET step and should contains the search query.
I can't understand such behaviour.
Spring should map HTML form attributes to your model: SearchForm.
Spring MVC build accordions with request parameters and your model object properties and set matching properties into your model Object before pass object into your controller method.
You named HTML property(and request parameter name automatically) as id="search". But SearchForm hasn't such property. Instead it has searchQuery property. So after Spring MVC unable to set searchQuery value into your SearchForm it pass model with null attribute.
Please Change th:value="{searchQuery}" to th:field="{searchQuery}".
I hope it'll work.
It worked for me:
FormTestController.java
#Controller
public class FormTestController {
#RequestMapping(value = "/form-test-1.jhtml", method = RequestMethod.GET)
public String formTest1(#ModelAttribute("form1") Form1TestVO form1TestVO, Model model){
System.out.println("You've submited: " + form1TestVO.getName())
model.addAttribute("form1", new Form1TestVO("Form 1 test"));
return "form-test-1";
}
}
form-test-1.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.thymeleaf.org" >
<head>
<title>Form test 1</title>
</head>
<body >
<form th:object="${form1}" th:action="#{/form-test-1.jhtml}" >
<input th:field="*{name}" />
<button>Send</button>
</form>
</body>
</html>
Form1TestVO
public class Form1TestVO {
private String name;
public Form1TestVO() {
}
public Form1TestVO(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Reference
Related
**Hi everyone. I ran into a problem while developing a web application. The saveEmployee method does not work properly and throws the corresponding error. In the project I use Spring-boot + Thymeleaf. I think there is an error in these two files or a problem in the configuration. But so far I haven't found anything.
**
myRestController.java
#Controller
#RequestMapping("/shop")
public class myRestController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/allEmployees")
public String allEmployees(Model model) {
List<Employee> employees = employeeService.getAllEmployees();
model.addAttribute("employeesList", employees);
return "allEmployees";
}
#GetMapping("/allEmployees/{name}")
public String getEmployeeByName(#PathVariable String name, Model model) {
List<Employee> employees = employeeService.findAllByName(name);
model.addAttribute("employeesList", employees);
return "allEmployees";
}
#GetMapping("/newEmployee")
public String addEmployee(Model model) {
Employee employee = new Employee();
model.addAttribute("employee", employee);
return "addNewEmployee";
}
#RequestMapping()
public String saveEmployee(#ModelAttribute("employee") Employee employee){
employeeService.saveEmployee(employee);
return "index";
}
addNewEmployee.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Add Employee</h1>
<form th:method="POST" th:action="#{shop/allEmployees}" th:object="${employee}">
<label for="name">Enter name:</label>
<input type="text" th:field="*{name}" id="name"/>
<br/>
<label for="surname">Enter surname:</label>
<input type="text" th:field="*{surname}" id="surname"/>
<br/>
<label for="department">Enter department:</label>
<input type="text" th:field="*{department}" id="department"/>
<br/>
<label for="salary">Enter salary:</label>
<input type="text" th:field="*{salary}" id="salary"/>
<br/>
<input type="submit" value="Create!">
</form>
<br><br>
</body>
</html>
In your myRestController.java, I am not seeing any #PostMapping defined. In addNewEmployee.html, it appears you are attempting to call shop/allEmployees with a POST rather than the GET method. If your intention is to pass a body or form to the shop/allEmployees endpoint, you may want to consider either changing your #GetMapping to a #PostMapping that accepts a #RequestBody or creating an entirely new #PostMapping that accepts a #RequestBody.
I have a Controller:
#Controller
public class ImageController {
#GetMapping("/upload")
public String uploadImageGet(Model model) {
return "uploadForm";
}
#PostMapping("/upload")
public String uploadImagePost(#ModelAttribute Image image, Model model) throws IOException {
// what should I do here?
return "result";
}
}
An HTML form:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="#" th:action="#{/upload}" th:object="${image}" method="post" enctype="multipart/form-data">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" value=""><br>
<label for="description">Description:</label><br>
<input type="text" id="description" name="description" value=""><br>
<label for="author">Author:</label><br>
<input type="text" id="author" name="author" value=""><br>
<label for="image">Image:</label><br>
<input type="file" id="image" name="image"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
And a class for storing the form data:
public class Image {
private String name;
private String author;
private String description;
private MultipartFile image;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
I want to send the form data to another host (Spring API), but also display a "Uploaded successfully" response page. Because of that I wanted to handle all that in the controller, but can't figure out how to do this. I was able to find some resources on creating a request manually, but this seems like there must be some simpler way to do this. If I'm approaching it wrong please let me know.
It hardly depends what you mean by:
I want to send the form data to another host (Spring API)
One approach would be to send the Data async with REST to another Spring application or any other application using springs rest template: https://www.baeldung.com/rest-template.
And show the user a succesfull html site.
But if you do it async you should keep in mind that the REST call could fail but you showed the user a successfull page..
My recommendation would be to create an additional REST Endpoint in your spring boot application and send the data from the site with an fetch or ajax call via javascript to it, from where it could be sent to the other spring application
It is simple page redirection example.When i click on it's button redirect page, it,s not working.It shows page not found.
final.jsp
<body>
<h2>Redirected Page</h2>
</body>
HelloController.java
#Controller
public class HelloController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model m) {
return "index";
}
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public String redirect() {
return "redirect:finalPage";
}
#RequestMapping(value = "/finalPage", method = RequestMethod.GET)
public String finalPage(Model m) {
return "final";
}
index.jsp
<body>
<h2>Spring Page Redirection</h2>
<p>Click below button to redirect the result to new page</p>
<form:form method="GET" action="/finalPage">
<table>
<tr>
<td>
<input type="submit" value="Redirect Page"/>
</td>
</tr>
</table>
</form:form>
</body>
web.xml
The returned redirect string is wrong, try: "redirect:/finalPage"
Missed / in redirect:finalpage. It should be something like this
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public String redirect() {
return "redirect:/finalPage";
}
But from where you are calling /redirect ?
Please also have a look here. Basically, you need to have a UrlBasedViewResolver subclass in your project and have the final view in there, if it's not in root.
I am new at Java. Using Thymeleaf and Spring-Boot.
Trying to show validation message on wrong input.
"Phone" property must be between 7 to 13 character long. Validation message will be shown if rules are not followed.
Please note, validation works but message is not shown.
Here is the Model
#Entity
public class Author {
#Column(name = "phone")
#Size(min=7, max = 13, message = "The category name must be {min} to {max} characters in length.")
private String phone;
}
the Controller
#Controller
#RequestMapping("/author")
public class AuthorController extends WebMvcConfigurerAdapter {
#Autowired
AuthorService authorService;
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/new-author").setViewName("newauthor");
}
#RequestMapping(value="/new-author", method = RequestMethod.GET)
public String newAuthor(Model model){
Author author = new Author();
model.addAttribute("addNewAuthor", author);
return "newauthor";
}
#RequestMapping(value="/new-author", method = RequestMethod.POST)
public String newAuthor(#Valid Author author, BindingResult bindingResult, Model model){
model.addAttribute("addNewAuthor", author);
if (bindingResult.hasErrors()) {
return "newauthor";
}
try{
authorService.createAuthor(author);
model.addAttribute("statusReport", "Author Saved");
}
catch (Exception e){
model.addAttribute("statusReport", "Author not Saved");
}
return "newauthor";
}
}
here is the View
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Author</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="../public/bootstrap-3.3.6- dist/css/bootstrap.css" th:href="#{/bootstrap-3.3.6-dist/css/bootstrap.css}"/>
</head>
<body>
<h1>Add New Author</h1>
<div class="col-lg-3" >
<form role="form" action="#" th:action="#{/author/new-author}" th:object="${addNewAuthor}" method="post">
<div th:class="form-group" th:classappend="${#fields.hasErrors('phone')}? 'has-error'">
<label>Phone</label>
<input class="form-control" type="text" th:field="*{phone}" placeholder="Enter author's phone number"/>
<p th:if="${#fields.hasErrors('phone')}" class="label label-danger" th:errors="*{phone}">Phone Error</p>
</div>
<button type="submit" class="btn btn-default">Add</button>
<button type="reset" class="btn btn-default">Reset</button>
<p th:text="${statusReport}" > </p>
</form>
</div>
</body>
</html>
Your addNewAuthor should have the #ModelAttribute annotation.
It should be :
public String newAuthor(
#Valid #ModelAttribute("addNewAuthor") Author author,
BindingResult bindingResult,
Model model) {
// ...
}
I suppose it's better to do it this way,
first, remove message from Size constraint
#Column(name = "phone")
#Size(min=7, max = 13)
private String phone;
Second, add message to the localization file (message.properties).
Size.author.phone=The category name must be {1} to {2} characters in length.
Another way :
#Column(name = "phone")
#Size(min=7, max = 13, message="{phone.size}")
private String phone;
In message.properties :
phone.size=The category name must be {1} to {2} characters in length.
I had written a code for the passing the parameter but in the login page it is not displaying the parameter can anyone help me?My home page code as shown below
<h1>
<form:form action="./loginPage" method="GET" >
<input type ="text" value="abc" id="name">
<input type ="submit" value ="Login">
</form:form>
</h1>
my controller page:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
return "home";
}
#RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public String redirect(#ModelAttribute("name")String name,BindingResult result,Model model) {
model.addAttribute(name);
if(result.hasErrors()){
return "home";
}else{
model.addAttribute("name",name);
return "loginPage";
}
}
My login page to display the parameter:
<h1>
Welcome to login Page
</h1>
<p>The value is:${name}</p>
When collecting input, there are a couple of ways to go, I'll go over two options here:
You can use a command object and bind that as an attribute in which case you would have something like this:
public class LoginForm {
#NotNull(message="Name is required")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In your controller you would:
#RequestMapping(value="/login", method= { RequestMethod.POST } )
public String redirect(#ModelAttribute("LoginForm") #Valid LoginForm loginForm, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()){
return "home";
}
model.addAttribute("name", loginForm.getName());
return "loginPage";
}
In your JSP (with bootstrap classes) something like this, where you bind the form object to the form element which allows you to then bind the attribute "name" to the input field.
<form:form method="post"
commandName="loginForm"
action="/login"
role="form">
<form:errors path="name" id="name-errors" element="div" cssClass="alert alert-danger" />
<form:input path="name" cssClass="form-control" placeholder="Your name" />
<input type="submit" value="Submit" class="btn btn-primary" />
</form:form>
You can use request parameter binding to just pluck a field from a query string or form. However the downside is you can't use build in form validation and you want to redraw a form after an error you'll need to pass all the parameters back to repopulate it manually.I
#RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public String redirect(#RequestParam(value="name", required=false) String name, Model model) {
if(name == null || name.length() == 0) {
return "home";
}
model.addAttribute("name",name);
return "loginPage";
}
For what it looks like your trying to do I'd recommend you use the command object binding approach. Also if you are using spring MVC for authentication, you should take a look at spring security. While the configuration can be a little frustrating it does provide a standard way to handle authentication and authorization.
http://docs.spring.io/autorepo/docs/spring-security/3.2.x/guides/hellomvc.html
I think that Model is request scoped. You will have to use Sessions