Thymeleaf fragment processor and Spring 5 - java

Could somebody, please, help with Thymeleaf? I need to create a template layout and I've got stuck with <div th:fragment="content"></div>. The point is layout:fragment doesn't replace code on the dashboard page. By the way, other processors like layout:decorate or th:replace works well. I'm using Intellij IDEA with Spring 2.5.4.
My template structure
#dashboard.html
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{admin/share/template.html}">
<head>
<title>Admin dashboard</title>
</head>
<body>
<div layout:fragment="content">
<h1>Admin Dashboard</h1>
</div>
</body>
</html>
#template.html
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<style th:replace="~{admin/share/style}"></style>
</head>
<body>
<div class="app-container app-theme-white body-tabs-shadow fixed-sidebar fixed-header">
<div th:replace="/admin/share/header"></div>
<div class="app-main">
<div th:replace="/admin/share/sidebar"></div>
<div class="app-main__outer">
<div class="app-main__inner">
<div th:fragment="content">
<p>Page content goes here</p>
</div>
</div>
</div>
</div>
<div th:replace="/admin/share/js"></div>
</div>
</body>
</html>
#pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.0.0</version>
</dependency>
#Thymeleaf.class
#Configuration
public class Thymeleaf {
#Bean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
}
Result of Dashboard page
UPDATE:
The issue is solved. Just need to use layout:fragment instead of th:fragment =)

Just need to use layout:fragment instead of th:fragment =)

Related

Unable to render thymleaf page from Spring-Boot controller. Prints only the return string in output

Issue : I have developed a Spring-Boot rest api which I can call from POSTMAN. Now I am requesting the same REST API method from a Thymleaf page. But it renders only a string. The actual page doesn't gets loaded..
This is my controller :
#RestController
#RefreshScope
#RequestMapping("/shopping")
public class ShoppingController {
#RequestMapping(value="/productList")
public String listAllProducts(ModelMap model){
logger.info("ShoppingMS : listAllProducts()");
ResponseEntity<List<Product>> responseProductList = prodServ.listAllProducts();
List<Product> products = responseProductList.getBody();
if(products.isEmpty()) {
logger.info("No Products Found");
}
logger.info("No of products fetched : " +products.size());
model.addAttribute("products", products);
return "productList";
}
}
This is my Thymleaf page productsList.html :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Product List</title>
<link rel="stylesheet" type="text/css" th:href="#{/css/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/menu"></th:block>
<div class="page-title">Product List</div>
<div class="product-preview-container"
th:each="prodInfo : ${products.list}">
<ul>
<li>Product Code: <span th:utext="${prodInfo.productCode}"></span></li>
<li>Product Name: <span th:utext="${prodInfo.productName}"></span></li>
<li>Product Price: <span th:utext="${#numbers.formatDecimal(prodInfo.productPrice,3,2,'COMMA')}"></span></li>
<li><a th:href="#{|/buyProduct?code=${prodInfo.code}|}">Buy Now</a></li>
</ul>
</div>
<br />
<div class="page-navigator">
<th:block th>
<a th:href="#{|/productList|}" class="nav-item"></a>
<span class="nav-item" > ... </span>
</th:block>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
Maven Dependency for Thymleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Project Structure Image
Output :
Calling URL : http://localhost:1000/shopping/productList
It returns only the string `productList` in the page.
Please tell me where I am doing wrong. I am able to render a index.html page. But not this page.
That's because you are using #RestController annotation on top of your controller, which is nothing but #Controller+#ResponseBody.
Try to use only #Controller and then based on your method use #ResponseBody exclusively.
For example . If you want to return json response body then use #ResponseBody on method.
If you want to render thymeleaf page then don't use #ResponseBody.

Spring Boot Thymeleaf: How to include html file

i have a problem with Spring Boot Thymeleaf. I want to include a simple html-file in another html file with Thymeleaf.
Here my main.html file:
In the middle of the codesnippet you can see the root-div, here i want to include the dashboard.html file.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Admin-Area</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="/backend/webjars/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="/backend/webjars/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 fixed-top mainnav navbar-expand-xl">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="../dashboard/dashboard.html">B2C-Shop</a>
<button type="button" class="navbar-toggler navbar-toggler-right">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container-fluid">
<div class="row">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div th:each="script: ${scripts}">
<div th:if="${script.endsWith('dashboard.html')}">
<div id="root" th:include="dashboard :: dashboard></div>
</div>
</div>
</main>
</div>
</div>
<script src="/backend/webjars/es5-shim/4.5.9/es5-shim.min.js"></script>
<script src="/backend/webjars/es6-shim/0.20.2/es6-shim.min.js"></script>
<script src="/backend/webjars/jquery/3.3.1/jquery.min.js"></script>
<script src="/backend/webjars/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="/backend/webjars/modernizr/2.8.3/modernizr.min.js"></script>
<div th:each="script: ${scripts}">
<div th:if="${script.endsWith('.js')}">
<script type="text/javascript" th:src="'/backend/js' + ${script}"></script>
</div>
</div>
</body>
</html>
And this is the dashboard.html file which i want to include in the main.html file.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="dashboard">
<h1>Welcome to B2C-Admin-Area</h1>
</div>
</html>
This is my DashboardController.java which i declare the dashboarb/dashboard.html file
The return methode "getTemplate()" references to backend folder in resources folder.
/**
*
* Render dash board
*
* #param model the attribute model
* #return the template file name
*/
#RequestMapping(value = "/backend/dashboard/dashboard.html", method = RequestMethod.GET)
public String dashboard(final Model model)
{
String[] script = {"/dashboard/dashboard.html"};
model.addAttribute(SCRIPT, script);
return getTemplate();
}
/**
* Gets the template file name.
*
* #return the template file name
*/
protected String getTemplate()
{
return "backend";
}
My folder stucture looks like this:
Do you have any idea to include this file?
Thank you.
Regards MWC.
First arrange your html files as shown in the below structure:
Then create fragment for import as :
suppose you saved below code in breadcrumb file in template/common folder:
<section class="content-header" th:fragment="breadcrumbfragment">
<ol class="breadcrumb">
<li><i class="fa fa-dashboard"></i> Home</li>
<li class="active">Next </li>
<li class="active">Next to Next</li>
</ol>
</section>
Now to import above fragment use following line:
<section class="content-header" th:replace="common/breadcrumb::breadcrumbfragment"></section>

Bootstrap is not picked up by SpringBoot with Thymleafe

I am trying to use Thymleafe and Bootstrap in SpringBoot application.
However, I could not understand why SpringBoot is not picking up Bootstrap.
When I am running my project it's just showing plain-old HTML and not picking Bootstrap.
pom.xml
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1" />
<title>Home</title>
<script type="text/javascript" src="webjars/jquery/3.0.0/jquery.min.js"></script>
<script type="text/javascript" src="webjars/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css"
href="webjars/bootstrap/4.0.0/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<h2>Please Login</h2>
<form th:action="#{/users/login}" method="POST">
<div class="form-group">
<label for="email">Email:</label> <input type="email"
class="form-control" id="username" placeholder="Enter email"
name="username"></input>
</div>
<div class="form-group">
<label for="pwd">Password:</label> <input type="password"
class="form-control" id="password" placeholder="Enter password"
name="password"></input>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
Can anyone please help what I am missing?
If your application is not running in the root context path or if the page is not served at the root path then your script and stylesheet references will not work because they are document-relative.
Consider building the URLs with Theamleaf's URL functionality, like this:
<link rel="stylesheet" th:href="#{/webjars/bootstrap/4.0.0/css/bootstrap.min.css}" />
Same for the <script> elements. Use th:src attribute there.
See here for more info:
http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#link-urls

Thymeleaf templates - Is there a way to decorate a template instead of including a template fragment?

I am working with Thymeleaf for the first time, and I need a clarification about the templates. If I correctly understand the documentation, I can include a template - or just a fragment of it - in my page. So for example, I can write something like that:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:include="template/layout :: header">
</head>
<body>
Hello world
<div th:include="template/layout :: footer"></div>
</body>
</html>
But what I want is in fact the opposite way of using the template : instead of including template fragment in the page, I want to include the page inside my template, something like that:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
...
</head>
<body>
<div id="my-template-header">...</div>
<div id="the-content">
<!-- include here the content of the current page visited by the user -->
???
</div>
<div id="my-template-footer">...</div>
</body>
In others words, is there a way to have an equivalent of the Sitemesh decorators tags in Thymeleaf?
Thanks
with Thymeleaf 2.1, you can write something like that:
Create the template (for ex. templates/layout.html), and add the th:fragment="page" information in html tag and define the content area with th:include="this :: content" information:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
th:fragment="page">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Test</title>
</head>
<body>
layout page
<div th:include="this :: content"/>
layout footer
</body>
</html>
Now create the page that will include this template adding th:include="templates/layout :: page" in html tag and put your main content inside a div with th:fragment="content"
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
th:include="templates/layout :: page">
<head>
<title></title>
</head>
<body>
<div th:fragment="content">
my page content
</div>
</body>
</html>
In layout page you can use this (th:include="this :: content") or suppress this option (th:include=":: content"). It seems like jsf facelets I think.
Ok, as stated by Sotirios Delimanolis, Thymeleaf does not support that way of using template, or should I say "Hierarchical layouts", as explained by Daniel Fernandez in this thread.
As sitemesh and Thymeleaf are compatible, it seems that I have to use both solutions. Too bad.
Edit: As suggested by DennisJaamann in a comment, I finally used Thymeleaf Layout Dialect, a view dialect that provides the feature I was looking for.
The working code:
First I add the LayoutDialect class:
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
//NB, selecting HTML5 as the template mode.
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
Then, I create the template (for ex. templates/layout.html), and add the layout:fragment information where I want to put the content of the current page:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
...
</head>
<body>
<div id="my-template-header">...</div>
<div id="the-content" layout:fragment="content">
<!-- include here the content of the current page visited by the user -->
</div>
<div id="my-template-footer">...</div>
</body>
and the page will refers to the template with the attribute layout:decorator:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="templates/layout">
<body>
<div layout:fragment="content">
Hello world
</div>
</body>
</html>
You need
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>1.2.2</version>
</dependency>
and add this to your SpringTemplateEngine:
#Bean
#Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new LayoutDialect());
return templateEngine;
}
If now create a folder named template in your views folder.
views/home.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="template/layout">
<body>
<div layout:fragment="content">
Hello world
</div>
</body>
</html>
views/layout/layout.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
</head>
<body>
<div id="content" layout:fragment="content">
</div>
</body>
</html>
As far as I know, you can't. A possible solution would be to create a ViewResolver that always forwards to your decorator file, but at the same time put Model attributes that would have the actual path to the fragment you want to include.

The resource required (servlet) not available

I have the following problem when trying to use the login.jsp
I have the following code in the login
<%# include file="/jsp/include.jsp"%>
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script>
function sendForm() {
document.formLogin.submit();
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Example :: Spring Application</title>
</head>
<body>
<div class="container">
<form class="bs-docs-example form-horizontal"
action="ServletValidation" name="formLogin" id="formLogin"
method="post">
<legend>Login</legend>
<div class="control-group">
<label for="inputUsername" class="control-label">Email</label>
<div class="controls">
<input type="text" id="inputUsername">
</div>
</div>
<div class="control-group">
<label for="inputPassword" class="control-label">Password</label>
<div class="controls">
<input type="password" id="inputPassword">
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox"> <input type="checkbox">
Remember me
</label>
<button class="btn" type="submit" action="sendForm();">Sign
in</button>
</div>
</div>
</form>
</div>
</body>
</html>
And the following text in the web.xml
<servlet>
<description></description>
<display-name>ValidationServlet</display-name>
<servlet-name>ValidationServlet</servlet-name>
<servlet-class>bt.servlet.ValidationServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ValidationServlet</servlet-name>
<url-pattern>/ValidationServlet</url-pattern>
</servlet-mapping>
But once I click the button for submit it returns:
State HTTP 404 - /bt/jsp/ServletValidation
Description The resource required (/bt/jsp/ServletValidation) is not
available.
The folder structure is the following:
+bt
-src
-WebContent
-jsp
-resources
-WEB-INF
-classes
*web.xml
*index.jsp
The problem I'm finding is why is it sending to that URL
There are two problems:
Your servlet maps to the URL /ValidationServlet and your form has the action set to ServletValidation.
Maybe your login.jsp isn't at the same level of your servlet mapping.
The best solution would be setting the action of your form to map the full URL that maps to your servlet. This can be achieved using Request#getContextPath():
<form action="${request.contextPath}/ValidationServlet" ...>
<!-- content... -->
</form>
If you don't use JSTL in your project, then do it. You should avoid scriptlets in your jsp (those <% ... %> tags that hold nasty Java code inside the JSP). But if you don't, then you should try the following:
<form action="<%=request.getContextPath()%>/ValidationServlet" ...>
<!-- content... -->
</form>
Still, the first way is the best to go.
More info:
How to avoid Java Code in JSP-Files?
Our JSTL wiki page
Just change the web.xml file
<url-pattern>/ServletValidation</url-pattern>
ServletValidation is different from ValidationServlet.

Categories