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

@ -27,3 +27,6 @@ http://localhost:8082/spring-thymeleaf/addStudent/
http://localhost:8082/spring-thymeleaf/listStudents/ http://localhost:8082/spring-thymeleaf/listStudents/
The first URL is the home page of the application. The home page has links to the other two pages. The first URL is the home page of the application. The home page has links to the other two pages.
### Security
The user/password required is: user1/user1Pass

View File

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

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

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

@ -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>")));
}
}