I am using Spring Boot with Thymeleaf. I want to get the value from the first form, display it on the second form where I get extra data and then, when the submit button on the 2nd form is pressed, have both values display in a result Page.
Neither BindingResult nor plain target object for bean name 'formTwo' available as request attribute
Update - I think I have sorted Point 2. now by using th:attr and changing my Controller slightly. I have updated the code snippets to reflect my progress so far.
This is what I have so far. What am I doing wrong?
The 1st form (One.html)
<body>
<form action="#" th:action="@{/}" th:object="${formOne}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
FormOne.class
public class FormOne {
@NotBlank
@Size (min=2, max=10)
private String name;
public FormOne() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "FormOne{" + "name=" + name + "}";
}
}
The 2nd form "Two.html"
<body>
<form th:action="@{/name_age}" th:object="${formTwo}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th.value="*{name}" /></td>
</tr><tr>
<td>Age:</td>
<!-- This has been removed <td><input type="text" th:value="*{age}" /></td> -->
<td><input type="text" th:name="name" th:attr="value = ${formOne.name}" th.field="*{name}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
</tr><tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
FormTwo.class
public class FormTwo {
private String name;
@NotNull
@Min(10)
private Integer age;
public FormTwo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "FormTwo{" + "name=" + name + ", age=" + age + "}";
}
}
AppController.class
@Controller
public class AppController {
@GetMapping("/")
public String showStart(FormOne formOne) {
return "One";
}
@PostMapping("/")
// public String checkStartInfo(@Valid FormOne formOne, BindingResult bindingResult) {
public ModelAndView checkStartInfo(@Valid FormOne formOne, BindingResult bindingResult) {
System.out.println("==Inside checkStartInfo() - " + formOne.toString());
ModelAndView mav = new ModelAndView();
if (bindingResult.hasErrors()) {
mav.setViewName("One");
return mav;
// return "One";
}
mav.setViewName("Two");
mav.addObject("formOne", formOne);
return mav;
// return "Two";
}
@PostMapping("name_age")
public String setName_Age(@Valid FormTwo formTwo, BindingResult bindingResult) {
System.out.println("==Inside setName_Age() - " + formTwo.toString());
if (bindingResult.hasErrors()) {
return "Two";
}
return "Result";
}
}
So I have found a way to get it to work. Comments if it is incorrect would be appreciated. I was making it really complicated. But below are my code snippets of how I got it to work.
The 1st form (One.html) - Remains unchanged
FormOne.class - Remains unchanged
The 2nd Form (Two.html) - Looks as follows:
<body>
<form action="#" th:action="@{/name_age}" th:object="${formTwo}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:name="name" th:attr="value = ${formTwo.name}" readonly/></td>
</tr><tr>
<td>Age:</td>
<td><input type="text" th:field="*{age}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
</tr><tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
FormTwo.class - Remains unchanged
AppController.class - This is where MOST of the changes happened.
@Controller
public class AppController {
@GetMapping("/")
public String showFormOne(FormOne formOne) {
return "One";
}
@PostMapping("/")
public String checkFormOneInfo(@Valid FormOne formOne, BindingResult bindingResult, Model m) {
if (bindingResult.hasErrors()) {
return "One";
}
FormTwo ft = new FormTwo();
ft.setName(formOne.getName());
m.addAttribute("formTwo", ft);
return "Two";
}
@GetMapping("/name_age")
public String showFormTwo(FormTwo formTwo) {
return "Two";
}
@PostMapping("/name_age")
public String checkPersonInfo(@Valid FormTwo formTwo, BindingResult bindingResult, Model m) {
if (bindingResult.hasErrors()) {
return "Two";
}
m.addAttribute("bothForms", formTwo);
return "Result";
}
}
In summary I had to do the following:
My Results.html are addressed in the following page: Results.html
<body>
Congratulations! You Got here!
<p th:text="'Name: ' + ${bothForms.name} + '!'" />
<p th:text="'Age: ' + ${bothForms.age} + '!'" />
</body>
This works for what I need it to do. I would be interested if any gurus had better ways of doing this.
Hope it helps someone in the future.