Thymeleaf CRUD - Real Time Project
© luv2code LLC
Application Requirements
From the Boss
Create a Web UI for the Employee Directory
Users should be able to
• Get a list of employees Thymeleaf + Spring Boo
t
• Add a new employee
• Update an employee
• Delete an employee
www.luv2code.com © luv2code LLC
Real-Time Project Thymeleaf + Spring Boo
t
www.luv2code.com © luv2code LLC
Big Picture
1 2 3 4
Employe Employe Employe
Controller Service Repository
Web
Browser
Thymeleaf Templates
www.luv2code.com © luv2code LLC
Application Architecture Reuse code from
previous project
Employee Employe Employe
Controller Service Repository
New code that we will create
www.luv2code.com © luv2code LLC
Project Set Up
• We will extend our existing Employee project and add DB integration
• Add EmployeeService, EmployeeRepository and Employee entit
• Available in one of our previous project
• We created all of this code already from scratch … so we'll just copy/paste it
• Allows us to focus on creating EmployeeController and Thymeleaf
templates
www.luv2code.com © luv2code LLC
Development Process - Big Picture Step-
By-S
tep
1. Get list of employee
2. Add a new employe
3. Update an existing employe
4. Delete an existing employee
www.luv2code.com © luv2code LLC
Thymeleaf - Add Employee
Date
© luv2code LLC
Add Employee - DEMO
www.luv2code.com © luv2code LLC
Add Employee Step-
By-S
tep
1. New Add Employee button for list-employees.html
www.luv2code.com © luv2code LLC
Add Employee Step-
By-S
tep
1. New Add Employee button for list-employees.html
2. Create HTML form for new employee
www.luv2code.com © luv2code LLC
Add Employee Step-
By-S
tep
1. New Add Employee button for list-employees.html
2. Create HTML form for new employee
3. Process form data to save employee
www.luv2code.com © luv2code LLC
Step 1: New "Add Employee" button
• Add Employee button will href link to
• request mapping /employees/showFormForAdd
<a th:href="@{/employees/showFormForAdd}">
Add Employee Add Employee
</a>
@ symbo
Reference context path of your application
(app root)
www.luv2code.com © luv2code LLC
Step 1: New "Add Employee" button
• Add Employee button will href link to
• request mapping /employees/showFormForAdd
<a th:href="@{/employees/showFormForAdd}"
<a th:href="@{/employees/showFormForAdd}">
class="btn btn-primary btn-sm mb-3">
Add Employee
Add Employee
</a>
</a>
Button
Apply Bootstrap styles Button Primary
Button Small
Docs on Bootstrap styles: www.getbootstrap.com Margin Bottom, 3 pixels
www.luv2code.com © luv2code LLC
Step 1: New "Add Employee" button
• Add Employee button will href link to
• request mapping /employees/showFormForAdd
<a th:href="@{/employees/showFormForAdd}"
class="btn btn-primary btn-sm mb-3">
Add Employee
</a>
TODO
Add controller request mapping for
/employees/showFormForAdd
www.luv2code.com © luv2code LLC
Showing Form
In your Spring Controller
• Before you show the form, you must add a model attribute
• This is an object that will hold form data for the data binding
www.luv2code.com © luv2code LLC
Controller code to show form
@Controller
@RequestMapping("/employees")
public class EmployeeController {
@GetMapping("/showFormForAdd") Our Thymleaf template will
public String showFormForAdd(Model theModel) { access this data for
binding form data
// create model attribute to bind form data
Employee theEmployee = new Employee();
theModel.addAttribute("employee", theEmployee);
return "employees/employee-form";
}
…
} src/main/resources/templates/employees/employee-form.html
www.luv2code.com © luv2code LLC
Thymeleaf and Spring MVC Data Binding
• Thymeleaf has special expressions for binding Spring MVC form data
• Automatically setting / retrieving data from a Java object
www.luv2code.com © luv2code LLC
Thymeleaf Expressions
• Thymeleaf expressions can help you build the HTML form :-)
Expression Description
th:action Location to send form data
th:object Reference to model attribute
th:field Bind input field to a property on model attribute
more …. See - www.luv2code.com/thymeleaf-create-form
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
Empty place holder Real work
Thymeleaf will handle real work Send form data to
/employees/save
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
</form>
Our model attribute
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
*{…}
Selects property on referenced
th:object
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<input type="text" th:field="*{firstName}" placeholder="First name">
<input type="text" th:field="*{lastName}" placeholder="Last name">
<input type="text" th:field="*{email}" placeholder="Email">
<button type="submit">Save</button>
</form>
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
1
<form action="#" th:action="@{/employees/save}" When form is loaded,
th:object="${employee}" method="POST"> will call:
<input type="text" th:field="*{firstName}" placeholder="First name"> employee.getFirstName()
employee.getLastName
<input type="text" th:field="*{lastName}" placeholder="Last name">
<input type="text" th:field="*{email}" placeholder="Email">
2
<button type="submit">Save</button>
When form is submitted,
</form> will call:
employee.setFirstName(…)
employee.setLastName(…)
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<input type="text" th:field="*{firstName}" placeholder="First name"
name">
class="form-control mb-4 w-25">
<input type="text" th:field="*{lastName}" placeholder="Last name">
Form control
<input type="text" th:field="*{email}" placeholder="Email">
Apply Bootstrap styles Margin Bottom: 4 pixels
Width: 25%
<button type="submit">Save</button>
</form>
</form>
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<input type="text" th:field="*{firstName}" placeholder="First name" Button
class="form-control mb-4 w-25"> Button Info
Column Span 2
<input type="text" th:field="*{lastName}" placeholder="Last name"
class="form-control mb-4 w-25">
<input type="text" th:field="*{email}" placeholder="Email"
class="form-control mb-4 w-25">
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
Apply Bootstrap styles
www.luv2code.com © luv2code LLC
Step 2: Create HTML form for new employee
TODO
<form action="#" th:action="@{/employees/save}" Add controller request mapping for
th:object="${employee}" method="POST"> /employees/save
<input type="text" th:field="*{firstName}" placeholder="First name"
class="form-control mb-4 w-25">
<input type="text" th:field="*{lastName}" placeholder="Last name"
class="form-control mb-4 w-25">
<input type="text" th:field="*{email}" placeholder="Email"
class="form-control mb-4 w-25">
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
www.luv2code.com © luv2code LLC
Step 3: Process form data to save employee
@Controller
Since only one constructor
@Autowired is optional
@RequestMapping("/employees")
public class EmployeeController {
private EmployeeService employeeService;
public EmployeeController(EmployeeService theEmployeeService) {
employeeService = theEmployeeService;
}
@PostMapping("/save") Constructor injection
public String saveEmployee(@ModelAttribute("employee") Employee theEmployee) {
// save the employee
employeeService.save(theEmployee);
// use a redirect to prevent duplicate submissions
return "redirect:/employees/list";
}
…
}
www.luv2code.com © luv2code LLC
Step 3: Process form data to save employee
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private EmployeeService employeeService;
public EmployeeController(EmployeeService theEmployeeService) {
employeeService = theEmployeeService;
}
@PostMapping("/save")
public String saveEmployee(@ModelAttribute("employee") Employee theEmployee) {
// save the employee
employeeService.save(theEmployee);
// use a redirect to prevent duplicate submissions
return "redirect:/employees/list";
}
…
}
www.luv2code.com © luv2code LLC
Step 3: Process form data to save employee
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private EmployeeService employeeService;
public EmployeeController(EmployeeService theEmployeeService) {
employeeService = theEmployeeService;
}
@PostMapping("/save")
public String saveEmployee(@ModelAttribute("employee") Employee theEmployee) {
// save the employee
employeeService.save(theEmployee);
// use a redirect to prevent duplicate submissions "Post/Redirect/Get" pattern
return "redirect:/employees/list";
}
}
…
For more info see
Redirect to request mapping www.luv2code.com/post-redirect-get
/employees/list
www.luv2code.com © luv2code LLC
Thymeleaf - Update Employee
Date
© luv2code LLC
Update Employee - Demo
www.luv2code.com © luv2code LLC
Update Employee Step-
By-S
tep
1. "Update" button
2. Pre-populate the form
3. Process form data
www.luv2code.com © luv2code LLC
Step 1: "Update" Button
Each row has an Update link
- current employee id embedded in link
When clicked
- will load the employee from database
- prepopulate the form
www.luv2code.com © luv2code LLC
Step 1: "Update" button
• Update button includes employee id
<tr th:each="tempEmployee : ${employees}">
…
<td>
<a th:href="@{/employees/showFormForUpdate(employeeId=${tempEmployee.id})}"
class="btn btn-info btn-sm">
Update
</a>
Appends to URL
</td>
?employeeId=xxx
</tr>
www.luv2code.com © luv2code LLC
Step 2: Pre-populate Form
@Controller
@RequestMapping("/employees")
public class EmployeeController {
…
@GetMapping("/showFormForUpdate")
public String showFormForUpdate(@RequestParam("employeeId") int theId,
Model theModel) {
// get the employee from the service
Employee theEmployee = employeeService.findById(theId);
// set employee as a model attribute to pre-populate the form
theModel.addAttribute("employee", theEmployee);
// send over to our form
return "employees/employee-form";
}
www.luv2code.com © luv2code LLC
Step 2: Pre-populate Form 1
When form is loaded,
will call:
employee.getFirstName()
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
employee.getLastName
<!-- Add hidden form field to handle update -->
<input type="hidden" th:field="*{id}" />
<input type="text" th:field="*{firstName}"
class="form-control mb-4 w-25" placeholder="First name"> This is how form is
pre-populated
<input type="text" th:field="*{lastName}" Thanks to calls to getters
class="form-control mb-4 w-25" placeholder="Last name">
<input type="text" th:field="*{email}"
class="form-control mb-4 w-25" placeholder="Email">
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
www.luv2code.com © luv2code LLC
Step 2: Pre-populate Form
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<!-- Add hidden form field to handle update -->
Hidden form field
<input type="hidden" th:field="*{id}" /> required for updates
<input type="text" th:field="*{firstName}"
class="form-control mb-4 w-25" placeholder="First name">
<input type="text" th:field="*{lastName}"
class="form-control mb-4 w-25" placeholder="Last name">
<input type="text" th:field="*{email}"
class="form-control mb-4 w-25" placeholder="Email">
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
www.luv2code.com © luv2code LLC
Step 2: Pre-populate Form
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<!-- Add hidden form field to handle update -->
<input type="hidden" th:field="*{id}" />
<input type="text" th:field="*{firstName}"
class="form-control mb-4 w-25" placeholder="First name">
<input type="text" th:field="*{lastName}"
class="form-control mb-4 w-25" placeholder="Last name">
<input type="text" th:field="*{email}"
class="form-control mb-4 w-25" placeholder="Email">
<button type="submit" class="btn btn-info col-2">Save</button>
</form>
www.luv2code.com © luv2code LLC
Step 2: Pre-populate Form
<form action="#" th:action="@{/employees/save}"
th:object="${employee}" method="POST">
<!-- Add hidden form field to handle update -->
<input type="hidden" th:field="*{id}" />
<input type="text" th:field="*{firstName}"
class="form-control mb-4 w-25" placeholder="First name">
<input type="text" th:field="*{lastName}"
class="form-control mb-4 w-25" placeholder="Last name">
<input type="text" th:field="*{email}" This binds to the model attribute
class="form-control mb-4 w-25" placeholder="Email">
Tells your app
<button type="submit" class="btn btn-info col-2">Save</button>
</form> which employee to update
www.luv2code.com © luv2code LLC
Step 3: Process form data to save employee
• No need for new code … we can reuse our existing cod
• Works the same for add or update :-)
@Controller
@RequestMapping("/employees")
public class EmployeeController {
…
@PostMapping("/save")
public String saveEmployee(@ModelAttribute("employee") Employee theEmployee) {
// save the employee
employeeService.save(theEmployee);
// use a redirect to prevent duplicate submissions
return "redirect:/employees/list";
}
…
}
www.luv2code.com © luv2code LLC
Thymeleaf - Delete Employee
Date
© luv2code LLC
Delete Employee - DEMO
www.luv2code.com © luv2code LLC
Delete Employee Step-
By-S
tep
1. Add “Delete” button/link on page
2. Add controller code for “Delete”
www.luv2code.com © luv2code LLC
Step 1: "Delete" button
Each row has a Delete button/link
- current employee id embedded in link
When clicked
- prompt user
- will delete the employee from database
www.luv2code.com © luv2code LLC
Step 1: "Delete" button
• Delete button includes employee id
Appends to URL
<tr th:each="tempEmployee : ${employees}">
…
?employeeId=xxx
<td>
<a th:href="@{/employees/delete(employeeId=${tempEmployee.id})}"
class="btn btn-danger btn-sm"
onclick="if (!(confirm('Are you sure you want to delete this employee?'))) return false">
Delete
</a>
</td>
</tr>
JavaScript to prompt user before deleting
www.luv2code.com © luv2code LLC
Step 2: Add controller code for delete
@Controller
@RequestMapping("/employees")
public class EmployeeController {
…
@GetMapping("/delete")
public String delete(@RequestParam("employeeId") int theId) {
// delete the employee
employeeService.deleteById(theId);
// redirect to /employees/list
return "redirect:/employees/list";
}
…
}
www.luv2code.com © luv2code LLC