For further reference, please consider the following sections:
- Official Gradle documentation
- Spring Boot Gradle Plugin Reference Guide
- Create an OCI image
- Spring Web
- Spring Data JPA
- Thymeleaf
The following guides illustrate how to use some features concretely:
- Building a RESTful Web Service
- Serving Web Content with Spring MVC
- Building REST services with Spring
- Accessing Data with JPA
- Accessing data with MySQL
- Handling Form Submission
++ src/main/resources/ ++ static -> css, js, html, etc ++ template -> themeleaf html ++ file.properties
Including:
- Templates Attributes : Thymeleaf Attributes(eg. th:text), HTML Attributes(eg. th:title)
- Standard Expressions : ${...}, *{...}, @{...}, ~{...}, #{...}
- Các fragment : th:insert, th:place
- Vòng lặp : th:each
- Đánh giá : th:if, th:unless, th:switch, th:case
- Hiệu chỉnh thuộc tính: th:attr, th:attrprepend, th: attrappend
- Hiệu chỉnh thuộc tính HTML: th:value, th:href
- Thay đổi nội dung thẻ: , th:text, th:text
- Khai báo fragment: th:fragment
- Xoá fragment: th:remove
${...}
: Variable Expressions ->Scope
andParameters
*{...}
: Selection Expressions -> Thuộc tính của th:object#{...}
: Message Expressions -> i18n resourse (.properties)@{...}
: URL expressions -> URL/URI (th:src, th:href)~{...}
: fragement expression -> layout(th:insert, th:replace)
<span th:text="${user.name}">
=> hiển thị thuộc tính name trong bean user trong model(request).<li th:each="book : ${books}" th:text = "${book.title}">
=> Duyệt tập hợp books trong model and hiện thị title trong mỗi book.<span th:text="${session.user.uname}">
=> Hiển thị thuộc tính name của bean user trong session scope<span th:text="${application.user.name" >
=> Hiển thị thuộc tính name của bean user trong context scope<span th:text="${param.name}">
=> Hiển thị tham số name
<ul th:object="${book}">
<li th:text = "*{title}"></li>
<li th:text = "*{publisher}"></li>
<li th:text = "${book.name}"></li>
</ul>
- menu.properties
menu.home = Home
- menu_vi.properties
menu.home = Trang chủ
<ul >
<li th:text = "#{menu.home}"></li>
</ul>
- Root relative URL (tương đối so với URL gốc-webroot)
<a th:href="@{/order/list}"></a>
==>
<a href="/ctxpath/order/list"></a>
- Page Relative URL (tương đối so vơi URL hiện tại)
<a th:href="@{../order}"></a>
==>
<a href="../order"></a>
<a th:href="@{order/list}"></a>
==>
<a href="order/list"></a>
- Protocol Relative And Absolute URL
<a th:href = "@{//www.poly.com/order/list}"></a>
==>
<a href = "//www.poly.com/order/list"></a>
<a th:href = "@{https://www.poly.com/order/list}"></a>
==>
<a href = "https://www.poly.com/order/list"></a>
- Parameters => Có 3 cách tạo ra
<th:book th:with="x='X', y='Y'">
<a th:href="@{/order/details(a=${x}, b=${y})}"></a>
<a th:href="@{|/order/details?a=${x}&b=${y}|}"></a>
<a th:href="@{'/order/details?a='+ ${x} + '&b=' + ${y}}"></a>
</th:book>
===>
<a href="/order/details?a=X&b=Y"></a>
- PathVariables =>
<th:block th:with="x='X', y='Y'>
<a th:href="@{/order/{a}/details/{b}(a=${x},b=${y})}"> </a>
<a th:href="@{|/order/${x}/details/${x}|}"> </a>
<a th:href="@{'/order/'+ ${a}+ '/details/' +${b}}"> </a>
</th:block>
===>
<a href="/order/X/details/Y"></a>
Explain:
th:block
: tạo khốith:with
: dùng để khai báo biến trong 1 block
Fragment Expressions được sử dụng để sao chép một file hoặc một fragment template đươc định nghĩa trước vào các vị trí mong muốn
<div th:insert="~{/menu.html}">
==> Thay thế nội dung thẻ div bằng nội dung file menu.html<div th:replace="~{/menu.html}">
==> Thay thế thẻ div bằng nội dung file menu.html<div th:insert="~{/fragments.html::menu}">
==> Thay thế nội dung thẻ div bằng fragment có tên là menu trong file fragments.html<div th:replace="~{/fragments.html::menu}">
==> Thay thế thẻ div bằng fragment có tên là menu trong file fragments.html
String
: + , |...${}...|(thymeleaf3), [[expr]], [(expr)]- Arithmetic: +, - , * , / , %
- Logic: and, or, not (!)
- Comparison: < (lt), <= (le), > (gt), >= (ge), == (eq), != (ne)
- Conditional: condition ? trueValue: falseValue Eg:
<ul th:with="x='X', y='<b><i>Y</i></b>'">
<li th:utext="${x} + ${y}"></li>
<li th:utext="| Value of y is ${y} |"></li>
<li>Hello [[${y}]]</li>
<li >[(${y})]</li>
</ul>
<ul>
<li th:text="${x+y}"></li>
<li th:text="${x%y}"></li>
<li th:text="${x*y}"></li>
<li th:text="${x - y}"></li>
<li th:text="${x / y}"></li>
<li th:text="not ${x>y} "></li>
<li th:text="${x gt y} and ${x==y} "></li>
<li th:text="${x > y} ? 'OK' : 'Cancel' "></li>
<li th:text="@{|/a/b/${x}/${y}|} "></li>
<li >[[${message}]]Hoang Hieu</li>
<li >Cong: [[${x+y}]]</li>
</ul>
<ul th:object="${bean}">
<li>Fullname: <b th:text="*{fullname}"></b></li>
<li>
Marks:
<b th:text="*{mark}"></b>
(<i th:text="*{mark >= 5.0 ? 'Passed':'Failed'"></i>)
</li>
<li>
<b th:if = "*{marks>9.0}">Golden Bee</b>
</li>
</ul>
- th:each
th:each="item: ${iterable}"
: iterable is a list- th:each="item, state: ${iterable}" : state including: index
- th:each="entry: ${iterable}" : entry is a key-value pair
- th:each="entry, state: ${iterable}" :
- th:if
th:if="expr"
: true (true, != null, !="" )th:unless="expr"
: if else
- th:switch
<any th:switch="expr">
<any th:case="v1"></any>
</any th:case="*" > same default in switch-case
Servlet Implicit Object
: Thymeleaf định nghĩa sẵn các đối tượng servlet:request
,response
,session
,serveletContext
,locale
, từ đó gọi các phương thúc để thục hiện:
${#request.getRequestURL()}
${#session.getId()}
${#servletContext.getContextPath()}
${#locale.getLanguages()}
Note: Thymeleaf cho phpes áp dụng theo quy ước JavaBean đối với các getter${#request.requestURL}
${#session.id}
${#servletContext.contextPath}
${#locale.language}
Impicit Uility Objects
Thymeleaf cung cấp các đối tượng ngầm định chứa các phương thức tiện ích hỗ trợ xử lý trong Template
#message
: Xử lý tài nguyên đa ngôn ngữ ==> eg. ${#message.msg('key', param1, param2)}#dates
: xử lý thời gian ==> eg. ${#dates.format(date, parttern)}#numbers
: Xử lý số ==> eg. ${#numbers.formatDecimal(marks, 0, 'WHITESPACE',2,'POINT')}#strings
: Xử lý chuỗi ==> eg. ${#strings.capitalizeWords(str)}#arrays
: xử lý mảng ==> eg. ${#arrays.length(array)}#lists
: xử lý danh sách ==> eg. ${#lists.size(list)}#aggregates:
tổng hợp dữ liệu từ collection, arrays, mapp ==> eg. ${#aggregates.sum(array)}
<ul th:object="${student}">
<li>
Fullname: <b th:text="*{#strings.capitalizeWords(fullname)}">
</b>
<li>
Marks: <b th:text="*{#numbers.formatDecimal(marks, 0, 'COMMA', 2, 'POINT')}">
</b>
<li>
Birthday: <b th:text="*{#dates.format(dob, 'dd-MM-yyyy')}">
</b>
</li>
</ul>
<body>
<h3> UTILITY OBJECT</h3>
<b>TỔNG HỢP</b>
<ul>
<li>
Today: <i th:text="${#dates.format(now, 'EEE, dd-MM-yyyy')}"></i>
</li>
<li>
Number of students : <i th:text="${#lists.size(students)}"></i>
</li>
</ul>
<h2>DANH SÁCH</h2>
<ul th:each="student: ${students}" th:object="${student}">
<li>
Name: <b th:text="*{#strings.capitalizeWords(name)}"></b>
</li>
<li>
Marks: <b th:text="*{#numbers.formatDecimal(marks, 0, 'COMMA', 2, 'POINT')}"></b>
</li>
<li>
Number of subjects: <b th:text="*{#arrays.length(subjects)}"></b>
</li>
</ul>
</body>
</html>
- HTML Thymeleaf
<form th:action="/validation" th:object="student">
</form>
2.Request Mapping
@RequestMapping("/validation")
3.Model and Validation Model
@Data
public class Student{
@NotBlank(message="Khong de trong email")
@Email(message="Khong dung dinh dang email")
private String email;
@NotBlank(messsage= "Khong de trong ho va ten")
private String fullname;
@NotNull(message = "Khong de trong diem")
@Max(value=10, message="Diem khong the tren 10")
@PositiveOrZero(message="Diem khong the am")
private Double marks;
@NotNull(message="Chua chon gioi tinh")
private Boolean gender;
@NotBlank(message= "Chua chon quoc tich")
private String country;
}
Note: NotBlank
=> String, NotEmpty
=> String, Collection, NotNull
=> Object
4. Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- Number Validation Annotaion
- Min, Max, DecimalMin, DecimalMax
- Positive, PositiveOrZero, Negative, NegativeOrZero
- Digits
- String Validation Annotaion
- Email, Pattern
- Not Blank
- List Validation Annotaion
- NotEmpty, Size
- Time Validation Annotaion
- Past, PastOrPresent, Future, FutureOrPresent
- Other
- NotNull, AssertFalse, AssertTrue, Null
- Thymeleaf Form Template
- Phan tu form va thuoc tinh Thymeleaf
<form th:action="@{url}" th:object="${bean}">
<input th:field="*{property}"/>
<input th:field="*{property}"/>
<select th:field="*{property}"/>
<textarea th:field="*{property}"/>
<any th:error="*{property}"/>
<button></button>
</form>
- Mot so the hoac thuoc tinh bo tro:
-
<label>
-
th:selected
-
th:disabled
-
th:readonly
- DEMO:
<form th:action="@{/validation}" th:object="${student}" method="post">
<input th:field="*{email}"/>
<input th:field="*{fullname}"/>
<input th:field="*{marks}"/>
<input th:field="*{gender}" type="radio" value="true"/>
<textarea th:field="*{property}"/>
<any th:error="*{property}"/>
<button>Validation</button>
</form>
- Validation Controller
@GetMapping(/validator)
public String validate(Model model, @ModelAttribute Student student){
return "valiation-form"
}
@PostMapping(/validator)
public String validate(Model model, @Valid @ModelAttribute Student student, Errors errors){
if (errors.hasErros()){
model.addAttribute("message", "Vui long sua cac loi sau");
}
else{
model.addAttribute("message", "Tat ca da hop le")
}
return "validation-form";
}
- Display errors
<form th:action="@{/validation}" th:object="${student}" method="post">
<input th:field="*{email}"/>
<i th:error="*{email}"></i>
<input th:field="*{fullname}"/>
<i th:error="*{fullname}"></i>
<input th:field="*{marks}"/>
<i th:error="*{marks}"></i>
<input th:field="*{gender}" type="radio" value="true"/>
<textarea th:field="*{property}"/>
<any th:error="*{property}"/>
<button>Validation</button>
</form>
- Using Select box
<select th:field="*{country}">
<option value=""></option>
<option th:each="c: ${countries}" th:value="${c.key}">[[${c.value}]]</option>
</select>
- ModelAttributes cho countries
@ModelAttributes("countries")
public Map<String, String > getCountries(){
Map<String, String> map = new HashMap<>();
map.put("VN", "VietNam");
map.put("US", "United States");
}
- Error Message Resource
@Data
public class Student{
@NotBlank
@Email
private String email;
}
- Tao file resource
-
Annotation.bean.property
syntax
-
<form th:object=${student}></form>
-
- student_en.properties
NotBlank.student.email = Email is required
....
-
- student_vi.properties
NotBlank.student.email = Khong de trong email
....
- Fragments
<!-- frags.html-->
<html xmlns:th=http://www.thymeleaf.org">
<div th:fragment="frag1">
<h3>Fragment 1</h3>
</div>
<div th:fragment="frag2(x,y)">
<h3>Fragment [[${x}]] and [[${y}]]</h3>
</div>
</html>
<!-- page.html-->
<!DOCTYPE html>
<html xmlns:th=http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body th:with="y='Hicode'">
<div th:replace="~{frags::frag1}"></div>
<hr>
<div th:replace="~{frags::frag2(~{::#x/text()}, ${y})}"></div>
<i id="x" th:remove="all">Hieu</i>
</body>
</html>
Explain:
~{frags::frag1}
: get frag1 in frags.html~{::#x/text()}
: get text in<i id="x" th:remove="all">Hieu</i>
- Syntax
- th:fragment:
th:fragment="name"
,th:fragment="name(params)"
- th:replace and th:insert
-
th:replace = "~{file::selector(param)}"
-
th:replace = "~{file::selector}"
-
th:replace = "~{file}"
-
th:replace = "~{::selector}"
==> tim slector phu hop trong trang do Note:
- Selector => including: fragment name, css selector
- Phan biet th:replace(thay the the do bang mang) va th:insert(lay mang bo vao giua the do)
- th:remove="all" => xoa 1 the va noi dung no khoi trang web
- Demo
<div th:fragment="frag1"></div>
<div th:fragment="frag2(x,y)"></div>
<div class="myclass"></div>
<div id="myid"></div>
<div th:replace="frags::frag1"></div>
<div th:replace="frags::frag2(~{::#x},)"></div>
<div th:replace="frags::.myclass"></div>
<div th:replace="frags::#myid"></div>
<i id="x" th:remove="all">Hieu</i>
- LayoutDesign
- layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:fragment="areas(title, body)">
<head>
<meta charset="utf-8">
<title th:replace="${title}"></title>
</head>
<body>
<div th:replace="~{menu:hnav}"></div>
<hr>
<div th:replace="${body}"></div>
</body>
</html>
- menu.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:fragment="areas(title, body)">
<nav th:fragment="hnav">
<a></a>
</nav>
</html>
- home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layout::areas(~{::title}, ~{::main})}">
<head>
<title>Home Pages</title>
</head>
<body>
<main>
<h1>This is home age</h1>
</main>
</body>
</html>
- Soạn tài nguyên đa ngôn ngữ
- Mã ngôn ngữ : _vi.properties, _en.properties
- Câu hình nạp từ nguyên đa ngôn ngữ vào hệ thống
import java.util.Locale;
@Configuration
public class MessageConfig implements WebMvcConfigurer {
@Bean("messageSource")
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setDefaultEncoding("UTF-8");
messageSource.setBasenames("classpath:menu", "classpath:i18n/menu");
return messageSource;
}
/**
* Maintain locale
* */
@Bean("localeResolve")
public LocalResolve getResolve() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setCookiePath("/");
resolver.setCookieMaxAge(10 * 24 * 60 * 60);
resolver.setDefaultLocale(new Locale("vi"));
return resolver;
}
@Override
public void addInterceptor(InterceptorRegistry registry) {
}
}
- Cấu hinh cách duy trì ngôn ngữ được chọn (cookie/session)
/**
* Maintain locale
* */
@Bean("localeResolve")
public LocalResolve getResolve() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setCookiePath("/");
resolver.setCookieMaxAge(10 * 24 * 60 * 60);
resolver.setDefaultLocale(new Locale("vi"));
return resolver;
}
- Hiển thị tài nguyên đa ngôn ngữ lên giao diện
<a th:href="@{/layout/index}">[[#{menu.home}]]</a>
- Cấu hinh tham số thay đổi ngôn ngữ (?lang)
@Override
public void addInterceptor(InterceptorRegistry registry) {
LocaleChangeInterceptor locale = new LocaleChangeInterceptor();
locale.setParamName("lang");
registry.addInterceptor(locale).addPathPatterns("/**");
}
url?lang=vi
url>lang=en
- Lựa chọn ngôn ngữ
- static
<a href="?lang=vi">Tieng anh</a>
<a href="?lang=en">Tieng viet</a>
- Ajax JQUERY
<script >
$(function () {
$("a[href*=lang").click(function () {
$ajax({
url: "/layout/home"+ $(this).attr("href")
})
.then(resp=>location.reload())
})
return false;
})
</script>