Cannot connect Thymeleaf HTML webpage to my Spring Boot API/DB - spring

I have a Spring Boot application for a school records management system. My API is working perfectly when using Insomnia but I cannot get my hTML webpage to interact with the API. Here are the relevant bits of my Staff entity/controller/repository/service. Any help appreciated!
Entity class:
#Entity
#Data
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
public class Staff {
#Id
#SequenceGenerator(
name = "STAFF_SEQ",
sequenceName = "STAFF_SEQ",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "STAFF_SEQ"
)
private Long staffId;
private String firstName;
private String lastName;
private String staffEmail;
#ManyToOne(targetEntity = Department.class)
private Long departmentId;
private String address;
private String contactNumber;
private char gender;
}
...
}
Controller Class
#RestController
public class StaffController {
#Autowired
private StaffService staffService;
#RequestMapping(value = "/saveStaff")
public Staff saveStaff(Staff staff) {
return staffService.saveStaff(staff);
}
...
}
Repository Class
#Repository
public interface StaffRepository extends JpaRepository<Staff, Long> {
Staff findByFirstNameAndLastName(String firstName, String lastName);
}
Service Interface
public interface StaffService {
public Staff saveStaff(Staff staff);
...
}
ServiceImpl
#Service
public class StaffServiceImpl implements StaffService{
#Autowired
StaffRepository staffRepository;
#Override
public Staff saveStaff(Staff staff) {
return staffRepository.save(staff);
}
...
}
HTML file
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<h2>Add Staff</h2>
<form action = "#" method="post" th:action ="#{/saveStaff}" th:object = "${staff}">
<fieldset>
<legend>Staff</legend>
<label for="firstName">First name:</label><br>
<input type="text" th:field ="*{firstName}" class="form-control" id="firstName" name="firstName"><br><br>
<label for="lastName">Last name:</label><br>
<input type="text" th:field ="*{lastName}" class="form-control" id="lastName" name="lastname"><br><br>
<label for="staffEmail">Staff email:</label><br>
<input type="staffEmail" th:field ="*{staffEmail}" class="form-control" id="staffEmail" name="staffEmail"> <br><br>
<label for="address">Address</label><br>
<input type="text" th:field ="*{address}" class="form-control" id="address" name="address"><br><br>
<label for="contactNumber"> Contact number</label><br>
<input type="text" th:field ="*{contactNumber}" class="form-control" id="contactNumber" name="contactNumber"><br><br>
<label for="gender">Gender:</label>
<select th:field ="*{gender}" class="form-control" id="gender" name="gender">
<option value="f">F</option>
<option value="m">M</option>
</select><br><br>
<label for="departmentId"> Department Id</label><br>
<input type="number" th:field ="*{departmentId}" class="form-control" id="departmentId" name="departmentId"><br><br>
<input type="submit" value="Submit">
<input type="reset">
</fieldset>
</form>
</body>
</html>

Your controller class is a #RestController. From view page you need to call the rest controller by using XmlHttpRequest or we can say, Ajax request or fetch API. For your ease, you can try jQuery. If you really want to submit your form the Thymeleaf way, then your controller needs to be a MVC Controller. Long story short, you should replace the annotation #RestController with #Controller. Also remove the action attribute and keep only th:action.
Your Controller class
#Controller
#RequestMapping("/course")
public class CourseController {
#Autowired
CourseService courseService;
#GetMapping("/add")
public String get_saveCourseForm(Model model) {
Course course = new Course();
model.addAttribute("course", course);
return "saveCourse";
}
#PostMapping("/save")
public String post_saveCourseForm(#ModelAttribute("course") Course course, Model model) {
courseService.saveCourse(course);
var courses = courseService.fetchCourseList();
model.addAttribute("courses", courses);
return "displayCourses";
}
}
Replaced #RestController with #Controller annotation
Add MVC methods and return view page names from the MVC methods. Also notice the request mappings. We will view the form by calling /course/add and the form will be submitted to /course/save.
Your Form Page
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<h2>Add Course</h2>
<form method="post" th:action ="#{/course/save}" th:object = "${course}">
<fieldset>
<legend>Staff</legend>
<label for="courseName">Course name:</label><br>
<input type="text" th:field ="*{courseName}" class="form-control" id="courseName" name="courseName"><br><br>
<label for="courseCode">Course code:</label><br>
<input type="text" th:field ="*{courseCode}" class="form-control" id="courseCode" name="courseCode"><br><br>
<label for="departmentId"> Department Id</label><br>
<input type="number" th:field ="*{departmentId}" class="form-control" id="departmentId" name="departmentId"><br><br>
<input type="submit" value="Submit">
<input type="reset">
</fieldset>
</form>
</body>
</html>
Check the form action.
The page will look like below.
When the form will be submitted, it will return the result page. the HTML code is as follows.
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Display Courses</title>
</head>
<body>
<table>
<tr>
<th>Course ID</th>
<th>Course Name</th>
<th>Course Code</th>
</tr>
<tr th:each="course : ${courses}">
<td><span th:text="${course.courseId}"></span></td>
<td><span th:text="${course.courseName}"></span></td>
<td><span th:text="${course.courseCode}"></span></td>
</tr>
</table>
</body>
</html>
The page will look as follows.

Related

how to validate email spring boot thymeleaf

I Want to validate e-mail when user want to register at my website. I set #NotBlank and #Email annotations, made #Valid annotation at post method, but I don't know how to make it work properly. At /register endpoint application shows error500. How can I change my code to make it work? Username shouldn't register with blank or incorrect email, but I can't solve it.
AppUser class:
#Data
#Entity
public class AppUser implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
#NotBlank(message = "login is required")
private String username;
#NotBlank(message = "password is required")
private String password;
private String role;
#Column(unique = true)
#Email(message = "Wrong e-mail")
private String email;
private String phone;
public AppUser() {
}
UserController class:
#Controller
public class UserController {
#GetMapping("register")
public String register(Model model) {
model.addAttribute("user", new AppUser());
return "register";
}
#PostMapping("/registerOk")
public String registerOk(#Valid AppUser appUser, Errors errors) {
if (errors.hasErrors()) {
return "register";
}
userService.addUser(appUser);
return "redirect:register?success";
}
}
My register.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Rejestracja</title>
<link rel="stylesheet" type="text/css" th:href="#{/css/index.css}"/>
</head>
<body>
<form class="box" th:action="#{/registerOk}" method="post">
<div th:if="${param.success}">
<div class="alert alert-info"><span style="color: white">Register is sucessful, check your e-mail adress.</span></div>
</div>
<h1>Register new account: </h1>
<input type="text" name="username" placeholder="login">
<span class="validationError"
th:if="${#fields.hasErrors('username')}"
th:errors="*{username}">Username Error</span>
<input type="password" name="password" placeholder="password" id="hide">
<input type="text" name="email" placeholder="E-Mail">
<input type="text" name="phone" placeholder="phone number">
<input type="submit" value="Register me!">
<div class="form-group">
<span style="color: white">Already registered? Login</span>
</div>
</form>
</body>
</html>
Use type="email" instead of text.
<input type="email" name="email" placeholder="E-Mail">
Use required if you would like the input field as mandatory input.
<input type="email" name="email" placeholder="E-Mail" required>

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>

Spring Boot, Thymeleaf Form Error

I am trying to create a task creation page. There is a form where the user can enter the name, description and task status and save it. However, when I go to the page it shows me this error
There was an unexpected error (type=Internal Server Error, status=500).
Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (index)
I also have a delete task in my program and that seems to work perfectly fine.
This is what my mainController looks like
#RequestMapping(value = "/save-task")
public String saveTask(#ModelAttribute("task") Task task, BindingResult bindingResult, Model model) {
task.setDateCreated(new Date());
taskService.save(task);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("task", new Task());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
#RequestMapping( value = "/delete-task")
public String deleteTask(#RequestParam (required = false) int id, Model model){
taskService.delete(id);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
Here is the form
<div class="container text-center">
<h3>Manage Task</h3>
<hr/>
<form class="form-horizontal" method="POST" th:action="#{/save-task}" th:object="${task}">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label class="control-label col-md-3">Name</label>
<div class="col-md-7">
<input type="text" class="form-control" name="name" th:field="*{name}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Description</label>
<div class="col-md-7">
<input type="text" class="form-control" name="description" th:field="*{description}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Finished</label>
<div class="col-md-7">
<input type="radio" class="col-sm-1" th:name="finished" value="true"/>
<div class="col-sm-1">Yes</div>
<input type="radio" class="col-sm-1" th:name="finished" value="false"/>
<div class="col-sm-1">No</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Save"/>
</div>
</div>
</form>
</div>
</div>
Delete Task html
<tr th:each="task: ${tasks}">
<td th:text="${task.id}"></td>
<td th:text="${task.name}"></td>
<td th:text="${task.description}"></td>
<td th:text="${task.dateCreated}"></td>
<td th:text="${task.finished}"></td>
<td><a th:href="#{'delete-task?id=' + ${task.id}}"><span class="glyphicon glyphicon-trash"></span></a> </td>
</tr>
Here is a part of my Task entity
#Entity
#Table(name = "T_TASKS")
public class Task implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "DATE_CREATED")
#Temporal(TemporalType.TIMESTAMP)
private Date dateCreated;
#Column(name = "FINISHED")
private boolean finished;
Here is the error in the slack trace
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'task' available as request attribute
Change your controller mapping to accept GET and POST requests. You're getting the error because Spring can't bind a bean to the form. So add a bean when GET is called.
#GetMapping("/save-task")
public String addTask(Model model) {
Task task = new Task();
task.setDateCreated(new Date();
model.addAttribute("task", task);
return "save-task"; //or whatever the page is called
}
Then do your processing:
#PostMapping("/save-task")
public String saveTask(#ModelAttribute("task") Task task,
BindingResult bindingResult,
Model model) {
taskService.save(task);
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
Your bean looks fine. I don't see that you use tasks in your HTML snippet, but that would go in the method mapped with #GetMapping if you're looking to just display all tasks.
Add the following to your controller class:
#ModelAttribute("task")
public Task newTaskObject() {
return new Task();
}
It will solve the binding problem. Also you need a post controller to handle the form submission.

How to update object with relation to list of objects via thymeleaf form

I am in trouble :/ I have one object that has a list of another objects and I want to change this list via select box and inputs, but when I submit this, the list is empty
Here is my code (model, thymelaf form, controller):
public class BetConfigVM {
private long id;
private String name;
private List<BetPriorityVM> betPriorities;
....getters and setters
}
public class BetPriorityVM {
private long id;
private CourseType courseType;
private BigDecimal minCourse;
private BigDecimal maxCourse;
....getters and setters
}
<form action="#" th:action="#{/betConfigSaveOrUpdate}" th:object="${config}" method="post">
<input type="text" th:field="*{name}" />
<span th:field="*{betPriorities}" th:each="prio : *{betPriorities}">
<select>
<option th:each="type : ${T(com.model.database.CourseType).values()}"
th:value="${type}" th:text="${type}" th:selected="${prio.courseType == type}">
</option>
</select>
<input type="text" th:field="${prio.minCourse}" />
<input type="text" th:field="${prio.maxCourse}" />
</span>
<input type="submit" th:value="Save" name="action"/>
</form>
#RequestMapping(value = "/betConfigSaveOrUpdate", method = RequestMethod.POST)
public String saveOrUpdateUser(#ModelAttribute("config") BetConfigVM configVM, #RequestParam String action, Model model) {
System.out.println(configVM.getName());
System.out.println("Size " + configVM.getBetPriorities().size());
model.addAttribute("config", configVM);
return "user/betConfigEdit";
}
Do you have any idea how to pass the changed list in the object?
EDIT:
Adding controller part that will show the form:
#RequestMapping(value = "/changeBetConfig", method = RequestMethod.GET)
public String changeBetConfig(Model model, #RequestParam("id") long id) {
BetConfig betConfig = betConfigRepository.findById(id);
BetConfigVM configVM = new BetConfigVM(betConfig);
model.addAttribute("config", configVM);
return "user/betConfigEdit";
}
I found a solution..the form has to look like this:
<form action="#" th:action="#{/betConfigSaveOrUpdate}" th:object="${config}" method="post">
<input type="text" th:field="*{name}" />
<span th:each="prio, rowStat : *{betPriorities}">
<select th:field="*{betPriorities[__${rowStat.index}__].courseType>
<option th:each="type : ${T(com.model.database.CourseType).values()}"
th:value="${type}" th:text="${type}" th:selected="${prio.courseType == type}">
</option>
</select>
<input type="text" th:field="*{betPriorities[__${rowStat.index}__].minCourse}" />
<input type="text" th:field="*{betPriorities[__${rowStat.index}__].maxCourse}" />
</span>
<input type="submit" th:value="Save" name="action"/>

Spring boot Mongodb Creating a nested document

can any one please tell me what I am missing ?
I am trying to create a Mongo collection using spring boot mongodb.
I want to create something like this
{
"_id":"123456"
"entity_name":"some name"
"entity_desc":"some description"
"events":[
{"title":"some title", "description":"some description"},
{"title":"title2", "description":"description2" }
]
}
but I am getting this
{
"_id":"123456"
"entity_name":"some name"
"entity_desc":"some description"
"events":[ ]
}
my Domain classes are
#Document
public class Entity {
#Id
private BigInteger id;
private String name, description;
private List<Event> events=new ArrayList<Event>();
/* GETTERS AND SETTERS */
}
public class Event {
private String titles;
private String descriptions;
/* GETTERS AND SETTERS */
}
and my repository is
public interface MyRepository extends MongoRepository<Entity, String>{
}
controller is
#Controller
public class RootController {
#Autowired
private MyRepository mr;
/* GET and other methods */
#RequestMapping(value="/", method=RequestMethod.POST)
public String helloPost(#ModelAttribute Entity entity){
mr.save(entity);
return "success";
}
}
and my jsp form is
<form:form modelAttribute="entity" role="form">
<div class="form-group">
Name <input type="text" id="name" name="name" /><br />
</div>
<div class="form-group">
Description <input type="text" id="description" name="description" /><br />
</div>
<div class="form-group">
event <input type="text" id="event" name="event" /><br />
</div>
<div class="form-group" >
title <input type="text" id="title" name="title" /><br />
</div>
<div class="form-group" >
description <input type="text" id="description" name="description" /><br />
</div>
<div>
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form:form>
Shouldn't it be something like below?
public class Event {
private String title;
private String description;
/* GETTERS AND SETTERS */
}
EDIT : Thanks for the jsp, clearly the way you are passing the values are not picked for child. Spring MVC doesn't support ONGL so it won't pick up child object with dot notation. But there are complex mechanism by setting path. Please look at this answer where it did pass child object list through form parameter. So it can be good starting point.

Resources