Thymeleaf with Spring Boot
© luv2code LLC
What is Thymeleaf?
• Thymeleaf is a Java templating engine www.thymeleaf.org
Separate project
Unrelated to spring.io
• Commonly used to generate the HTML views for web apps
• However, it is a general purpose templating engine
• Can use Thymeleaf outside of web apps (more on this later)
www.luv2code.com © luv2code LLC
What is a Thymeleaf template?
• Can be an HTML page with some Thymeleaf expression
• Include dynamic content from Thymeleaf expressions
HTML code Can access
Java code, objects
Thymeleaf expressions Spring beans
www.luv2code.com © luv2code LLC
Where is the Thymeleaf template processed?
• In a web app, Thymeleaf is processed on the serve
• Results included in HTML returned to browser
www.luv2code.com © luv2code LLC
Thymeleaf Demo
Output
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Add Thymeleaf to Maven POM le
fi
2. Develop Spring MVC Controller
3. Create Thymeleaf template
www.luv2code.com © luv2code LLC
Step 1: Add Thymeleaf to Maven pom file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Based on this,
Spring Boot will auto con gure to
fi
use Thymeleaf templates
www.luv2code.com © luv2code LLC
Step 2: Develop Spring MVC Controller
File: DemoController.java
@Controller
public class DemoController {
@GetMapping("/")
public String sayHello(Model theModel) {
theModel.addAttribute("theDate", new java.util.Date());
return "helloworld";
}
}
src/main/resources/templates/helloworld.html
www.luv2code.com © luv2code LLC
Where to place Thymeleaf template?
• In Spring Boot, your Thymeleaf template les go in
fi
• src/main/resources/templates
• For web apps, Thymeleaf templates have a .html extension
www.luv2code.com © luv2code LLC
Step 3: Create Thymeleaf template
File: DemoController.java
Thymeleaf accesses "theDate" @Controller
public class DemoController {
from the
Spring MVC Model @GetMapping("/")
public String sayHello(Model theModel) {
File: src/main/resources/templates/helloworld.html theModel.addAttribute("theDate", new java.util.Date());
To use Thymeleaf
<!DOCTYPE HTML> expressions
return "helloworld";
<html xmlns:th="http://www.thymeleaf.org"> } 1
<head> … </head> }
<body>
<p th:text="'Time on the server is ' + ${theDate}" />
</body>
</html>
2
Thymeleaf
expression
www.luv2code.com © luv2code LLC
Step 3: Create Thymeleaf template
File: DemoController.java
Thymeleaf accesses "theDate" @Controller
public class DemoController {
from the
Spring MVC Model @GetMapping("/")
public String sayHello(Model theModel) {
File: src/main/resources/templates/helloworld.html theModel.addAttribute("theDate", new java.util.Date());
<!DOCTYPE HTML> return "helloworld";
<html xmlns:th="http://www.thymeleaf.org"> } 1
<head> … </head> }
<body>
<p th:text="'Time on the server is ' + ${theDate}" />
</body>
</html>
2
Thymeleaf
expression
www.luv2code.com © luv2code LLC
Additional Features
• Looping and conditional
• CSS and JavaScript integratio
• Template layouts and fragments
www.thymeleaf.org
www.luv2code.com © luv2code LLC
CSS and Thymeleaf
© luv2code LLC
Let's Apply CSS Styles to our Page
Before After
Time on the server is Sat Jan 05 11:42:13 EST 2019 Time on the server is Sat Jan 05 11:42:13 EST 2019
font-style: italic;
color: green;
www.luv2code.com © luv2code LLC
Using CSS with Thymleaf Templates
• You have the option of using
• Local CSS les as part of your projec
fi
t
• Referencing remote CSS les
fi
• We'll cover both options in this video
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Create CSS le
fi
2. Reference CSS in Thymeleaf template
3. Apply CSS style
www.luv2code.com © luv2code LLC
Step 1: Create CSS file
• Spring Boot will look for static resources in the director
• src/main/resources/static You can create your own
custom sub-directorie
static/css
static/images
static/js
etc
src/main/resources
static
File: demo.css
css
.funny {
demo.css font-style: italic;
color: green;
}
Can be any sub-directory nam
e
www.luv2code.com © luv2code LLC
Step 2: Reference CSS in Thymeleaf template
File: helloworld.html @ symbo
<head> Reference context path of your application
<title>Thymeleaf Demo</title> (app root)
<!-- reference CSS file -->
<link rel="stylesheet" th:href="@{/css/demo.css}" />
</head>
src/main/resources
static
css
demo.css
www.luv2code.com © luv2code LLC
Step 3: Apply CSS
File: demo.css
File: helloworld.html
.funny {
<head> font-style: italic;
<title>Thymeleaf Demo</title> color: green;
}
<!-- reference CSS file -->
<link rel="stylesheet" th:href="@{/css/demo.css}" />
</head>
<body>
<p th:text="'Time on the server is ' + ${theDate}" class="funny" />
</body>
Time on the server is Sat Jan 05 11:42:13 EST 2019
www.luv2code.com © luv2code LLC
Other search directories
Spring Boot will search following directories for static resources
/src/main/resources
1. /META-INF/resources
2. /resources
Search order: top-down
3. /static
4. /public
www.luv2code.com © luv2code LLC
3rd Party CSS Libraries - Bootstrap
• Local Installatio
• Download Bootstrap le(s) and add to /static/css directory
fi
<head>
… …
<!-- reference CSS file -->
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />
</head>
www.luv2code.com © luv2code LLC
3rd Party CSS Libraries - Bootstrap
• Remote Files
<head>
… …
<!-- reference CSS file -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
… …
</head>
www.luv2code.com © luv2code LLC
Spring MVC Form Validation
© luv2code LLC
The Need for Validation
Check the user input form fo
required eld
fi
s
valid numbers in a rang
valid format (postal code
custom business rule
www.luv2code.com © luv2code LLC
Java’s Standard Bean Validation API
• Java has a standard Bean Validation AP
• De nes a metadata model and API for entity validation
fi
• Spring Boot and Thymeleaf also support the Bean Validation API
http://www.beanvalidation.org
www.luv2code.com © luv2code LLC
Bean Validation Features
Validation Feature
required
validate length
validate numbers
validate with regular expressions
custom validation
www.luv2code.com © luv2code LLC
Validation Annotations
www.luv2code.com © luv2code LLC
Our Road Map
1. set up our development environment
2. required eld
fi
3. validate number range: min, max
4. validate using regular expression (regexp)
5. custom validation
www.luv2code.com © luv2code LLC
Spring MVC Form Validatio
Required Fields
© luv2code LLC
Required Fields
www.luv2code.com © luv2code LLC
Pulling It All Together
customer-form.html
Customer
Custome
Controller
Customer
customer-con rmation.html
fi
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Create Customer class and add validation rules
2. Add Controller code to show HTML form
3. Develop HTML form and add validation support
4. Perform validation in the Controller class
5. Create con rmation page
fi
www.luv2code.com © luv2code LLC
Step 1: Create Customer class and add validation rules
File: Customer.java
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Size
public class Customer
private String firstName
@NotNull(message = "is required"
Validation rules
@Size(min=1, message = "is required"
private String lastName = “”
// getter/setter methods
…
www.luv2code.com © luv2code LLC
Step 2: Add Controller code to show HTML form
File: CustomerController.java
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.ui.Model
Model allows us to share
@Controlle information between Controllers
r
public class CustomerController and view pages (Thymeleaf)
@GetMapping("/"
)
public String showForm(Model theModel)
theModel.addAttribute("customer", new Customer())
return "customer-form"
;
name value
}
www.luv2code.com © luv2code LLC
Step 3: Develop HTML form and add validation support
Where to submit form data Model attribute name
File: customer-form.html
<form th:action="@{/processForm}" th:object="${customer}" method="POST">
First name: <input type="text" th:field="*{firstName}" / Property name from
>
Customer class
<br><br>
Last name (*): <input type="text" th:field="*{lastName}" / Property name from
Customer class
>
<!-- Show error message (if present) --
>
<span th:if="${#fields.hasErrors('lastName')}
"
th:errors="*{lastName} "
class="error"></span>
<br><br>
<input type="submit" value="Submit" />
</form>
www.luv2code.com © luv2code LLC
Step 4: Perform validation in Controller class
File: CustomerController.java
Model attribute name
@PostMapping("/processForm"
Tell Spring MVC to public String processForm
perform validation @Valid @ModelAttribute("customer") Customer theCustomer
BindingResult theBindingResult)
The results of if (theBindingResult.hasErrors())
validation
return "customer-form"
else
{
return "customer-confirmation"
www.luv2code.com © luv2code LLC
Step 5: Create confirmation page
File: customer-con rmation.html
fi
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
The customer is confirmed: <span th:text="${customer.firstName + ' ' + customer.lastName}" /
>
</body>
</html>
www.luv2code.com © luv2code LLC
Spring MVC Validatio
@InitBinder
© luv2code LLC
White Space
• Our previous example had a problem with white spac
• Last name eld with all whitespace passed … YIKES!
fi
• Should have failed!
• We need to trim whitespace from input elds
fi
www.luv2code.com © luv2code LLC
@InitBinder Adva
nce d
• @InitBinder annotation works as a pre-processor
• It will pre-process each web request to our controller
• Method annotated with @InitBinder is executed
www.luv2code.com © luv2code LLC
@InitBinder
• We will use this to trim String
• Remove leading and trailing white space
• If String only has white spaces … trim it to null
• Will resolve our validation problem … whew :-)
www.luv2code.com © luv2code LLC
Register Custom Editor in Controller
CustomerController.java
…
@InitBinde
r
public void initBinder(WebDataBinder dataBinder)
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true)
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor)
www.luv2code.com © luv2code LLC
Spring MVC Validatio
Number Range: @Min and @Max
© luv2code LLC
Validate a Number Range
• Add a new input eld on our form for: Free Passes
fi
• User can only enter a range: 0 to 10
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Add validation rule to Customer class
2. Display error messages on HTML form
3. Perform validation in the Controller class
4. Update con rmation page
fi
www.luv2code.com © luv2code LLC
Step 1: Add validation rule to Customer class
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.Max
public class Customer
@Min(value=0, message="must be greater than or equal to zero"
@Max(value=10, message="must be less than or equal to 10"
private int freePasses
;
// getter/setter method
s
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Add validation rule to Customer class
2. Display error messages on HTML form
3. Perform validation in the Controller class
4. Update con rmation page
fi
www.luv2code.com © luv2code LLC
Spring MVC Validatio
Regular Expressions
© luv2code LLC
Regular Expressions Adva
nce d
• A sequence of characters that de ne a search patter
fi
n
• This pattern is used to nd or match strings
fi
• Regular Expressions is like its own language (advanced topic
• I will assume you already know about regular expressions
• If not, then plenty of free tutorials availabl
• https://docs.oracle.com/javase/tutorial/essential/regex/
www.luv2code.com © luv2code LLC
Validate a Postal Code
• Add a new input eld on our form for: Postal Code
fi
• User can only enter 5 chars / digit
• Apply Regular Expression
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Add validation rule to Customer class
2. Display error messages on HTML form
3. Update con rmation page
fi
www.luv2code.com © luv2code LLC
Step 1: Add validation rule to Customer class
Adva
nce d
import jakarta.validation.constraints.Pattern
public class Customer
@Pattern(regexp="^[a-zA-Z0-9]{5}", message="only 5 chars/digits"
private String postalCode
// getter/setter method
s
www.luv2code.com © luv2code LLC
Spring MVC Validatio
Make an Integer Field Required
© luv2code LLC
Spring MVC Validatio
Handle String Input for Integer Fields
© luv2code LLC
Development Process Step-
By-S
tep
1. Create custom error messag
src/resources/messages.properties
www.luv2code.com © luv2code LLC
Spring MVC Validatio
Custom Validation
© luv2code LLC
Custom Validation Demo
www.luv2code.com © luv2code LLC
Custom Validation
✤ Perform custom validation based on your business rules
✤ Our example: Course Code must start with “LUV”
✤ Spring MVC calls our custom validation
✤ Custom validation returns boolean value for pass/fail (true/false)
www.luv2code.com © luv2code LLC
Create a custom Java Annotation … from scratch
Adva
nce d
✤ So far, we’ve used prede ned validation rules: @Min, @Max,
fi
…
✤ For custom validation … we will create a Custom Java Annotatio
✤ @CourseCod
e
@CourseCode(value="LUV", message="must start with LUV")
private String courseCode
www.luv2code.com © luv2code LLC
Development Process Step-
By-S
tep
1. Create custom validation rule
2. Add validation rule to Customer class
3. Display error messages on HTML form
4. Update con rmation page
fi
www.luv2code.com © luv2code LLC
Development Process - Drill Down
Step-
By-S
1. Create custom validation rul tep
a. Create @CourseCode annotatio
b. Create CourseCodeConstraintValidator
www.luv2code.com © luv2code LLC
Step 1a: Create @CourseCode annotation Adva
nce d
Usage Example
@CourseCode(value="LUV", message="must start with LUV")
private String courseCode;
www.luv2code.com © luv2code LLC
Step 1a: Create @CourseCode annotation Adva
nce d
@Constraint(validatedBy = CourseCodeConstraintValidator.class
@Target( { ElementType.METHOD, ElementType.FIELD }
@Retention(RetentionPolicy.RUNTIME
public @interface CourseCode
..
.
} @CourseCode(value="LUV", message="must start with LUV")
private String courseCode;
www.luv2code.com © luv2code LLC
Step 1a: Create @CourseCode annotation Adva
nce d
@Constraint(validatedBy = CourseCodeConstraintValidator.class
@Target( { ElementType.METHOD, ElementType.FIELD }
@Retention(RetentionPolicy.RUNTIME
public @interface CourseCode
// de ne default course code
fi
public String value() default "LUV"
..
.
} @CourseCode(value="LUV", message="must start with LUV")
private String courseCode;
www.luv2code.com © luv2code LLC
Step 1a: Create @CourseCode annotation Adva
nce d
@Constraint(validatedBy = CourseCodeConstraintValidator.class
@Target( { ElementType.METHOD, ElementType.FIELD }
@Retention(RetentionPolicy.RUNTIME
public @interface CourseCode
// de ne default course code
fi
public String value() default "LUV"
// de ne default error message
fi
public String message() default "must start with LUV"
..
.
} @CourseCode(value="LUV", message="must start with LUV")
private String courseCode;
www.luv2code.com © luv2code LLC
Step 1b: Create CourseCodeConstraintValidator Adva
nce d
import jakarta.validation.ConstraintValidator
import jakarta.validation.ConstraintValidatorContext
public class CourseCodeConstraintValidator
implements ConstraintValidator<CourseCode, String>
private String coursePre x
fi
;
@Override
public void initialize(CourseCode theCourseCode)
coursePre x = theCourseCode.value()
fi
;
@Override
public boolean isValid(String theCode,
ConstraintValidatorContext theConstraintValidatorContext)
boolean result
;
if (theCode != null)
{
result = theCode.startsWith(coursePre x)
fi
;
else
{
result = true
;
return result
;
www.luv2code.com © luv2code LLC