BAEL-1562 - Working with Fragments in Thymeleaf (#3868)

* BAEL-1562 - Thymeleaf sample working

* BAEL-1562 Code added for Fragments sample

* BAEL-1562 - Last correction for the test

* BAEL-1562 - Thymeleaf sample working

* BAEL-1562 Code added for Fragments sample

* BAEL-1562 - Last correction for the test
This commit is contained in:
Pello Altadill
2018-04-06 04:46:47 +02:00
committed by KevinGilmore
parent b20a3a1097
commit 5edb9acb95
18 changed files with 573 additions and 315 deletions

View File

@@ -77,14 +77,14 @@ public class WebMVCConfig extends WebMvcConfigurerAdapter implements Application
return resolver;
}
private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addDialect(new LayoutDialect(new GroupingStrategy()));
engine.addDialect(new Java8TimeDialect());
engine.setTemplateResolver(templateResolver);
engine.setTemplateEngineMessageSource(messageSource());
return engine;
}
private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addDialect(new LayoutDialect(new GroupingStrategy()));
engine.addDialect(new Java8TimeDialect());
engine.setTemplateResolver(templateResolver);
engine.setTemplateEngineMessageSource(messageSource());
return engine;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
@@ -142,7 +142,8 @@ public class WebMVCConfig extends WebMvcConfigurerAdapter implements Application
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/");
registry.addResourceHandler("/resources/**", "/css/**")
.addResourceLocations("/WEB-INF/resources/", "/WEB-INF/css/");
}
@Override

View File

@@ -0,0 +1,33 @@
package com.baeldung.thymeleaf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.baeldung.thymeleaf.utils.StudentUtils;
@Controller
public class FragmentsController {
@GetMapping("/fragments")
public String getHome() {
return "fragments.html";
}
@GetMapping("/markup")
public String markupPage() {
return "markup.html";
}
@GetMapping("/params")
public String paramsPage() {
return "params.html";
}
@GetMapping("/other")
public String otherPage(Model model) {
model.addAttribute("data", StudentUtils.buildStudents());
return "other.html";
}
}

View File

@@ -1,9 +1,9 @@
msg.id=ID
msg.name=Name
msg.gender=Gender
msg.percent=Percentage
welcome.message=Welcome Student !!!
msg.AddStudent=Add Student
msg.ListStudents=List Students
msg.Home=Home
msg.id=ID
msg.name=Name
msg.gender=Gender
msg.percent=Percentage
welcome.message=Welcome Student !!!
msg.AddStudent=Add Student
msg.ListStudents=List Students
msg.Home=Home

View File

@@ -0,0 +1,13 @@
body {
background-color: lightGray
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: #f1f1f1;
color: #ccc;
}

View File

@@ -1,43 +1,43 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
<h1>Add Student</h1>
<form action="#" th:action="@{/saveStudent}" th:object="${student}"
method="post">
<ul>
<li th:errors="*{id}" />
<li th:errors="*{name}" />
<li th:errors="*{gender}" />
<li th:errors="*{percentage}" />
</ul>
<table border="1">
<tr>
<td><label th:text="#{msg.id}" /></td>
<td><input type="number" th:field="*{id}" /></td>
</tr>
<tr>
<td><label th:text="#{msg.name}" /></td>
<td><input type="text" th:field="*{name}" /></td>
</tr>
<tr>
<td><label th:text="#{msg.gender}" /></td>
<td><select th:field="*{gender}">
<option th:value="'M'" th:text="Male"></option>
<option th:value="'F'" th:text="Female"></option>
</select></td>
</tr>
<tr>
<td><label th:text="#{msg.percent}" /></td>
<td><input type="text" th:field="*{percentage}" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
<h1>Add Student</h1>
<form action="#" th:action="@{/saveStudent}" th:object="${student}"
method="post">
<ul>
<li th:errors="*{id}" />
<li th:errors="*{name}" />
<li th:errors="*{gender}" />
<li th:errors="*{percentage}" />
</ul>
<table border="1">
<tr>
<td><label th:text="#{msg.id}" /></td>
<td><input type="number" th:field="*{id}" /></td>
</tr>
<tr>
<td><label th:text="#{msg.name}" /></td>
<td><input type="text" th:field="*{name}" /></td>
</tr>
<tr>
<td><label th:text="#{msg.gender}" /></td>
<td><select th:field="*{gender}">
<option th:value="'M'" th:text="Male"></option>
<option th:value="'F'" th:text="Female"></option>
</select></td>
</tr>
<tr>
<td><label th:text="#{msg.percent}" /></td>
<td><input type="text" th:field="*{percentage}" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
</form>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf Fragments: home</title>
<!--/*/ <th:block th:include="fragments/general.html :: headerfiles"></th:block> /*/-->
</head>
<body>
<header th:insert="fragments/general.html :: header"> </header>
<p>Go to the next page to see fragments in action</p>
<div th:replace="fragments/general.html :: footer"></div>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<div th:fragment="formField (field, value, size)">
<div>
<label th:for="${#strings.toLowerCase(field)}"> <span
th:text="${field}">Field</span>
</label>
</div>
<div>
<input type="text" th:id="${#strings.toLowerCase(field)}"
th:name="${#strings.toLowerCase(field)}" th:value="${value}"
th:size="${size}">
</div>
</div>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="headerfiles">
<meta charset="UTF-8" />
<link th:href="@{/css/styles.css}" rel="stylesheet">
</head>
<body>
<div th:fragment="header">
<h1>Thymeleaf Fragments sample</h1>
</div>
<p>Go to the next page to see fragments in action</p>
<aside>
<div>This is a sidebar</div>
</aside>
<div class="another">This is another sidebar</div>
<footer th:fragment="footer">
<a th:href="@{/fragments}">Fragments Index</a> |
<a th:href="@{/markup}">Markup inclussion</a> |
<a th:href="@{/params}">Fragment params</a> |
<a th:href="@{/other}">Other</a>
</footer>
</body>
</html>

View File

@@ -0,0 +1,2 @@
<div th:fragment="dataPresent">Data received</div>
<div th:fragment="noData">No data</div>

View File

@@ -0,0 +1 @@
<div><p id="parag">This is a subtitle</p></div>

View File

@@ -0,0 +1,14 @@
<table>
<thead th:fragment="fields(theadFields)">
<tr th:replace="${theadFields}">
</tr>
</thead>
<tbody th:fragment="tableBody(tableData)">
<tr th:each="row: ${tableData}">
<td th:text="${row.id}">0</td>
<td th:text="${row.name}">Name</td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>

View File

@@ -1,45 +1,45 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Student List</title>
</head>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script>
$(document).ready(function() {
$.ajax({
url : "/spring-thymeleaf/js",
});
});
</script>
<body>
<h1>Student List</h1>
<table border="1">
<thead>
<tr>
<th th:text="#{msg.id}" />
<th th:text="#{msg.name}" />
<th th:text="#{msg.gender}" />
<th th:text="#{msg.percent}" />
</tr>
</thead>
<tbody>
<tr th:each="student: ${students}">
<td th:text="${student.id}" />
<td th:text="${{student.name}}" />
<td th:switch="${student.gender}"><span th:case="'M'"
th:text="Male" /> <span th:case="'F'" th:text="Female" /></td>
<td th:text="${#conversions.convert(student.percentage, 'Integer')}" />
</tr>
</tbody>
</table>
<div>
<p>
<a th:href="@{/}" th:text="#{msg.Home}" />
</p>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Student List</title>
</head>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script>
$(document).ready(function() {
$.ajax({
url : "/spring-thymeleaf/js",
});
});
</script>
<body>
<h1>Student List</h1>
<table border="1">
<thead>
<tr>
<th th:text="#{msg.id}" />
<th th:text="#{msg.name}" />
<th th:text="#{msg.gender}" />
<th th:text="#{msg.percent}" />
</tr>
</thead>
<tbody>
<tr th:each="student: ${students}">
<td th:text="${student.id}" />
<td th:text="${{student.name}}" />
<td th:switch="${student.gender}"><span th:case="'M'"
th:text="Male" /> <span th:case="'F'" th:text="Female" /></td>
<td th:text="${#conversions.convert(student.percentage, 'Integer')}" />
</tr>
</tbody>
</table>
<div>
<p>
<a th:href="@{/}" th:text="#{msg.Home}" />
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Thymeleaf Fragments: markup</title>
</head>
<body>
<header th:insert="fragments/general.html :: header"> </header>
<div th:replace="fragments/general.html :: aside"></div>
<div th:replace="fragments/general.html :: div.another"></div>
<div th:replace="fragments/general.html :: footer"></div>
</body>
</html>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf Fragments: other</title>
</head>
<body>
<header th:replace="fragments/general.html :: header"> </header>
<div
th:replace="${#lists.size(data) > 0} ?
~{fragments/menus.html :: dataPresent} :
~{fragments/menus.html :: noData}">
</div>
<table>
<thead
th:replace="fragments/tables.html :: fields(~{ :: .myFields})">
<tr class="myFields">
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<div
th:replace="fragments/tables.html :: tableBody(tableData=${data})"></div>
</table>
<div th:replace="fragments/general.html :: footer"></div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Thymeleaf Fragments: params</title>
</head>
<body>
<header th:insert="fragments/general.html :: header"> </header>
<div
th:replace="fragments/forms.html :: formField(field='Name', value='John Doe',size='40')">
</div>
<div th:replace="fragments/general.html :: footer"></div>
</body>
</html>

View File

@@ -0,0 +1,90 @@
package com.baeldung.thymeleaf.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.baeldung.thymeleaf.config.InitSecurity;
import com.baeldung.thymeleaf.config.WebApp;
import com.baeldung.thymeleaf.config.WebMVCConfig;
import com.baeldung.thymeleaf.config.WebMVCSecurity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import javax.servlet.Filter;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class FragmentsTest {
@Autowired
WebApplicationContext wac;
@Autowired
MockHttpSession session;
private MockMvc mockMvc;
@Autowired
private Filter springSecurityFilterChain;
private RequestPostProcessor testUser() {
return user("user1").password("user1Pass").roles("USER");
}
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build();
}
@Test
public void whenAccessingFragmentsRoute_thenViewHasExpectedContent() throws Exception {
this.mockMvc
.perform(get("/fragments").with(testUser()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("<title>Thymeleaf Fragments: home</title>")));
}
@Test
public void whenAccessingParamsRoute_thenViewHasExpectedContent() throws Exception {
this.mockMvc
.perform(get("/params").with(testUser()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("<span>Name</span>")));
}
@Test
public void whenAccessingMarkupRoute_thenViewHasExpectedContent() throws Exception {
this.mockMvc
.perform(get("/markup").with(testUser()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("<div class=\"another\">This is another sidebar</div>")));
}
@Test
public void whenAccessingOtherRoute_thenViewHasExpectedContent() throws Exception {
this.mockMvc
.perform(get("/other").with(testUser()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("<td>John Smith</td>")));
}
}