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:
parent
b20a3a1097
commit
5edb9acb95
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
body {
|
||||||
|
background-color: lightGray
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<div th:fragment="dataPresent">Data received</div>
|
||||||
|
<div th:fragment="noData">No data</div>
|
|
@ -0,0 +1 @@
|
||||||
|
<div><p id="parag">This is a subtitle</p></div>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>")));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue